一、ActiveMQ介绍
1. ActiveMQ持久化机制
- ActiveMQ作为一个消息中间件,提供了持久化机制功能,并且是实时持久化的。
- 当持久化机制开启后,ActiveMQ中间件服务器宕机时,保证队列中的消息不会被丢失。
- SpringBoot整合ActiveMQ默认开启了持久化机制。
2. ActiveMQ可靠消息机制
- JMS默认是自动签收消息的,消费者获取到消息后,无论消费者对该消息处理业务逻辑是否成功,都会默认为已经消费的。
那么如何保证消息被消费成功呢?
- 使用手动签收模式:
消息中间件将消息推送给消费者,消费者接收到消息后,必须受手动发送命令告诉消息中间件消费成功,否则消息中间件将重新推送这条消息。 - 以事务形式发送或者接收:
生产者发送消息需要提交事务,消费者消费成功消息后也需要提交事务,这样就自动签收表示消费成功,消息中间件会删除这条消息。如果消费者没有提交事务,则消息中间件不会删除消息,而是当消息中间件有新的消息后,连同上次为成功消费的消息一起推送,消费者获取到消息进行重新消费。
3. ActiveMQ遵循JMS规范
JMS通讯模式:
- 发布/订阅模式(sub/pub 一对多
topic
): 如果消费者集群的话,每个消费者都会进行消费。 - 消息队列(p2p 一对一
queue
):如果消费者集群的话,每个消费者均摊进行消费(轮询)。
二、SpringBoot整合ActiveMQ(点对点模式)
希望每条消息都被一个消费者成功消费使用点对点模式,并且使用自动签收或者事务提交。
生产者:
- 引入Maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>activemq-queue-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activemq-queue-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- activemq依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 引入 application.yml配置
spring:
activemq:
broker-url: tcp://127.0.0.1:61616 # ActiveMQ消息中间件服务器通讯地址
user: admin # 用户名
password: admin #密码
queue: springboot-queue #自定义队列名,等下程序中会用到
server:
port: 8080
- 创建QueueConfig
@Configuration
public class QueueConfig {
//获取队列名
@Value("${queue}")
private String queue;
//注册bean
@Bean
public Queue logQueue() {
return new ActiveMQQueue(queue);
}
}
- 创建Producer
@Component
@EnableScheduling
public class Producer {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Queue queue;
//每隔5秒向消息队列发送消息
@Scheduled(fixedDelay = 5000)
public void send() {
jmsMessagingTemplate.convertAndSend(queue,"基于点对点发送消息: "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
- 启动类
@SpringBootApplication
@EnableScheduling //开启任务调度
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
消费者:
- 引入 maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>activemq-queue-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activemq-queue-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- activemq依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 引入 YML配置
spring:
activemq:
broker-url: tcp://127.0.0.1:61616 # ActiveMQ消息中间件服务器通讯地址
user: admin #用户名
password: admin #密码
queue: springboot-queue #自定义队列名,等下程序中会用到
server:
port: 8081
- 创建Consumer
@Component
public class Consumer {
@Value("${server.port}")
private String port;
@JmsListener(destination = "${queue}") //监听的队列,配置文件中的queue属性
public void receive(String msg){
System.out.println("点对点模式消费者"+port+"收到msg: " + msg);
}
}
- 启动类
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
- 测试
我们进入ActiveMQ管理页面可以看到一些数据:
两个消费者分摊消费(默认轮询,一条消息只被消费一次):
三、SpringBoot整合ActiveMQ(发布与订阅模式)
希望条消息被多个消费者或不被任何消费者消费,采用发布订阅模式。
发布与订阅模式其实与点对点使用方式基本一样,需要改的地方如下:
Producer修改:
- 修改yml配置:删除以前的自定义队列名,新增自定义主题名:
topic: spring-topic
- 配置类注册类型为Topic,即使用到Queue的地方都改成Topic
Consumer修改:
详细教程:
生产者:
- 引入Maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>activemq-topic-producer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activemq-topic-producer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- activemq依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 引入 application.yml配置
spring:
activemq:
broker-url: tcp://127.0.0.1:61616 # ActiveMQ消息中间件服务器通讯地址
user: admin # 用户名
password: admin #密码
topic: springboot-topic #自定义主题名,等下程序中会用到
server:
port: 8080
- 创建TopicConfig
@Configuration
public class TopicConfig {
//获取主题名
@Value("${topic}")
private String topic;
//注册bean
@Bean
public Topic logQueue() {
return new ActiveMQTopic(topic);
}
}
- 创建Producer
@Component
@EnableScheduling
public class Producer {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Autowired
private Topic topic;
//每隔5秒向消息队列发送消息
@Scheduled(fixedDelay = 5000)
public void send(){
jmsMessagingTemplate.convertAndSend(topic,"基于发布/订阅发送消息: "+ new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}
- 启动类
@EnableScheduling
@SpringBootApplication
public class ActivemqTopicApplication {
public static void main(String[] args) {
SpringApplication.run(ActivemqTopicApplication.class, args);
}
}
生产者:
- 引入Maven依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>activemq-topic-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>activemq-topic-consumer</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- activemq依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!-- SpringBoot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 引入 application.yml配置( 注意开启发布/订阅,否者消费者无法获取到消息)
spring:
activemq:
broker-url: tcp://127.0.0.1:61616 # ActiveMQ消息中间件服务器通讯地址
user: admin # 用户名
password: admin #密码
# 开启发布订阅(注意:SpringBoot整合ActiveMQ默认没有开启发布/订阅模式,因此需要手动配置 点对点模式是自动开启的)
jms:
pub-sub-domain: true
topic: springboot-topic #自定义主题名,等下程序中会用到
server:
port: 8082
- 创建Consumer
@Component
public class Consumer {
@Value("${server.port}")
private String port;
@JmsListener(destination = "${topic}") //监听的队列,配置文件中的queue属性
public void receive(String msg){
System.out.println("发布/订阅模式消费者"+port+"收到msg: " + msg);
}
}
- 启动类
@SpringBootApplication
public class ActivemqTopicConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ActivemqTopicConsumerApplication.class, args);
}
}
测试
先启动两个消费者订阅消息(订阅前的消息是无法被消费的),端口分别为8081,8082; 然后再启动生产者。
ActiveMQ管理页面可以看到一些数据:
可以看到出队数量大于入队数量,这是说明两个消费者都消费了消息,只要订阅了该主题的消费者都会消费消息。
两个消费者都进行了消费(说明只要订阅了该主题的消费者都会对消息进行消费,一条消息被消费多次):
--------------------------------- 学习不易,需要坚持 ---------------------------------