《ActiveMQ系列》3.SpringBoot整合Activemq
1 准备环境
- 新建springboot项目,引入以下
maven
依赖,我新建的springboot版本为2.6.14
。本应该有提供者和消费者两个项目,我这里用controller代替提供者。
<dependencies>
<!-- activemq依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!--消息队列连接池-->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
<version>5.15.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 不写测试类可以删除 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2 配置ActiveMQ
2.1 配置yml
server:
port: 9003
spring:
activemq:
broker-url: tcp://localhost:61616
# 消息不放在内存中
in-memory: false
pool:
# true表示使用连接池
enabled: true
# 连接池最大连接数
max-connections: 10
user: admin
password: admin
2.2 创建配置类
@Configuration
public class ActiveMQConfig {
@Value("${spring.activemq.broker-url}")
private String brokerUrl;
/**
* 创建连接工厂
* @return
*/
@Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
// 设置所以实体类被activemq信任,消息类型为对象时必须设置,否则会报错:MessageConversionException
factory.setTrustAllPackages(true);
return factory;
}
}
2.3 项目结构
3 发送文本信息
3.1 配置信息
ActiveMQConfig
中增加以下配置
/**
* 点对点队列
* @return
*/
@Bean
public Queue queue() {
return new ActiveMQQueue("QueueTest");
}
/**
* 发布订阅主题
* @return
*/
@Bean
public Topic topic(){
return new ActiveMQTopic("TopicTest");
}
/**
* Queue模式连接注入(点对点)
*/
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerQueue(ActiveMQConnectionFactory connectionFactory){
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
bean.setConnectionFactory(connectionFactory);
return bean;
}
/**
* Topic模式连接注入
*/
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerTopic(ActiveMQConnectionFactory connectionFactory){
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
//设置为发布订阅方式, 默认情况下使用的生产消费者方式
bean.setPubSubDomain(true);
bean.setConnectionFactory(connectionFactory);
return bean;
}
3.2 提供者
新建ProducerController
提供者,代码如下
@Controller
public class ProducerController {
//注入点对点的模式(Queue模式)
@Autowired
private Queue queue;
//注入订阅模式(Topic)的消息
@Autowired
private Topic topic;
//注入springboot封装的工具类
@Autowired
private JmsMessagingTemplate jms;
/**
* 点对点模式(queue)模式发消息
* @param text
*/
@GetMapping("/queueSend")
@ResponseBody
public String queueSend(String text) {
//发送消息至消息中间件
jms.convertAndSend(queue, text);
return "queue send ok";
}
/**
* 订阅模式(topic)发送消息
* @param text
* @return
*/
@RequestMapping("/topicSend")
@ResponseBody
public String topicSend(String text) {
jms.convertAndSend(topic,text);
return "topic send ok";
}
}
3.3 消费者
分别新建队列和订阅消费者,代码如下
队列消费者:
/**
* 消息消費者(Queue模式)
*/
@Component
public class ConsumerQueue {
/**
* 使用JmsListener配置消费者监听的队列,text是接收到的消息
* @param text
*/
@JmsListener(destination = "QueueTest", containerFactory = "jmsListenerContainerQueue")
public void receiveQueue(String text) {
System.out.println("Queue1消费者收到消息:" + text);
}
}
订阅消费者:
/**
* 消息消費者(Topic模式)
*/
@Component
public class ConsumerTopic {
/**
* 使用JmsListener配置消费者监听的队列,text是接收到的消息
* @param text
*/
@JmsListener(destination = "TopicTest", containerFactory = "jmsListenerContainerTopic")
public void receiveTopic1(String text) {
System.out.println("Topic消费者1收到消息:" + text);
}
@JmsListener(destination = "TopicTest", containerFactory = "jmsListenerContainerTopic")
public void receiveTopic2(String text) {
System.out.println("Topic消费者2收到消息:" + text);
}
}
3.4 测试
队列测试:http://localhost:9003/queueSend?text=helloworld
控制台输出结果:
Queue消费者收到消息:helloworld
发布订阅测试:http://localhost:9003/topicSend?text=helloworld
Topic消费者2收到消息:helloworld
Topic消费者1收到消息:helloworld
3.5 小结
- 当发送队列(Queue)消息时,只允许一个消费者来消费这些消息。如果存在多个消费者,则它们将会以轮询的方式竞争消费消息。
- 在发布订阅模式中,消息的发送者(发布者)将消息发送到一个主题(Topic),然后所有订阅该主题的接收者(订阅者)都会收到这个消息的副本。
- 持久化模式的发布订阅只能有一个消费者。
- 在发布订阅模式中,如果某个消费者处于离线或者宕机状态时,此时提供者发布的消息会丢失,因为该消费者无法接收到消息。(持久化订阅可以解决该问题)
4 发送对象信息
4.1 配置信息
ActiveMQConfig
中增加以下配置,队列和订阅的容器工厂用之前创建的即可。
/**
* 点对点队列(发送对象信息)
* @return
*/
@Bean
public Queue queueUser() {
return new ActiveMQQueue("QueueUser");
}
/**
* 发布订阅主题(发送对象信息)
* @return
*/
@Bean
public Topic topicUser(){
return new ActiveMQTopic("TopicUser");
}
4.2 创建实体类
创建实体类,用来发送对象类型消息。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private String id;
private String name;
private String sex;
}
4.3 提供者
ProducerController
增加如下配置
// 注入点对点模式(发送对象信息)
@Autowired
private Queue queueUser;
// 注入订阅模式(发送对象信息)
@Autowired
private Topic topicUser;
/**
* 点对点模式(queue)模式发消息
*/
@GetMapping("/queueSendUser")
@ResponseBody
public String queueSendUser() {
User user = new User("1", "张三", "男");
jms.convertAndSend(queueUser,user);
return "queue send user ok";
}
/**
* 订阅模式(topic)发送消息
* @return
*/
@RequestMapping("/topicSendUser")
@ResponseBody
public String topicSendUser() {
User user = new User("1", "张三", "男");
jms.convertAndSend(topicUser,user);
return "topic send user ok";
}
4.4 消费者
队列和订阅消费者,增加如下配置
ConsumerQueue
/**
* 使用JmsListener配置消费者监听的队列,接收对象消息
* @param user
*/
@JmsListener(destination = "QueueUser", containerFactory = "jmsListenerContainerQueue")
public void receiveQueue(User user) {
System.out.println("Queue消费者收到对象消息:" + user);
}
ConsumerTopic
/**
* 使用JmsListener配置消费者监听的队列,接收对象消息
* @param user
*/
@JmsListener(destination = "TopicUser", containerFactory = "jmsListenerContainerTopic")
public void receiveTopicUser1(User user) {
System.out.println("Topic消费者1收到对象消息:" + user);
}
@JmsListener(destination = "TopicUser", containerFactory = "jmsListenerContainerTopic")
public void receiveTopicUser2(User user) {
System.out.println("Topic消费者2收到对象消息:" + user);
}
4.5 测试
队列测试:http://localhost:9003/queueSendUser
Queue消费者收到对象消息:User(id=1, name=张三, sex=男)
发布订阅测试:http://localhost:9003/topicSendUser
Topic消费者2收到对象消息:User(id=1, name=张三, sex=男)
Topic消费者1收到对象消息:User(id=1, name=张三, sex=男)
5 持久化订阅
5.1 配置信息
ActiveMQConfig
中增加以下配置,使用持久化主题之前请先配置JDBC持久化(参考ActiveMQ安装教程)
/**
* 发布订阅主题(持久化主题)
* @return
*/
@Bean
public Topic topicDurable(){
return new ActiveMQTopic("topicDurable");
}
/**
* Topic模式连接注入,持久化配置
*/
@Bean
public JmsListenerContainerFactory<?> jmsListenerContainerTopicDurable(ActiveMQConnectionFactory connectionFactory){
DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
//设置为发布订阅方式, 默认情况下使用的生产消费者方式
bean.setPubSubDomain(true);
bean.setConnectionFactory(connectionFactory);
// 设置持久化,每个持久化的订阅都需要设置一个clientId
bean.setSubscriptionDurable(true);
bean.setClientId("myClientId");
return bean;
}
5.2 提供者
ProducerController
增加如下配置
/**
* 持久化订阅发送消息
* @return
*/
@RequestMapping("/subSend")
@ResponseBody
public String topicSendUser(String text) {
jms.convertAndSend(topicDurable,text);
return "topic send durable ok";
}
5.3 消费者
ConsumerTopic
增加如下配置
/**
* 接收持久化订阅消息
* @param text
*/
@JmsListener(destination = "topicDurable", containerFactory = "jmsListenerContainerTopicDurable")
public void receiveTopicDurable(String text) {
System.out.println("持久化Topic消费者收到消息:" + text);
}
5.4 测试
访问ActiveMQ页面,有如下内容,代表持久化订阅注册成功。
发布订阅测试:http://localhost:9003/subSend?text=helloworld
持久化Topic消费者收到消息:helloworld
5.5 小结
当使用持久化主题时,订阅该主题的消费者不仅可以接收到发布者发送的当前消息,还可以接收到之前发送的消息。这是因为服务器会将所有发布到持久化主题的消息保存到持久化存储中,以便在重启后重新加载这些消息并传递给消费者。
相比之下,非持久化主题只能传递当前消息,如果服务器在消息传递过程中宕机或重启,那么未被消费的消息将会丢失。
如果需要源码,私信我,我看到会回复你们。