Spring Boot中集成ActiveMQ
JMS和ActiveMQ的概念
- JMS即java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
- JMS只是接口,不同的供应商和开源组织对其有不同的实现,ActiveMQ就是其中之一,它支持JMS,是Apache推出的。
- JMS中存在多个对象模型:
- 连接工厂:ConnectionFactory
- JMS连接:Connect
- JMS会话:Session
- JMS目的:Destination
- JMS生产者:Producer
- JMS消费者:Consumer
- JMS消息两种类型:点对点和发布/订阅
- 可以看出 JMS 实际上和 JDBC 有点类似,JDBC 是可以⽤来访问许多不同关系数据库的 API,⽽ JMS 则提供同样与⼚商⽆关的访问⽅
法,以访问消息收发服务。 - ActiveMQ是Apache的一个开源消息总线,ActiveMQ 完全⽀持JMS1.1和J2EE 1.4规范,尽管 JMS 规范出台已经是很久
的事情了,但是 JMS 在当今的 Java EE 应⽤中间仍然扮演着特殊的地位。ActiveMQ ⽤在异步消息的处理上,所谓异步消息即消息发送者
⽆需等待消息接收者的处理以及返回,甚⾄⽆需关⼼消息是否发送成功。 - 异步消息主要有两种⽬的地形式,队列(queue)和主题(topic),队列⽤于点对点形式的消息通信,主题⽤于发布/订阅式的消息通信。
ActiveMQ的下载和安装
-
下载地址:https://activemq.apache.org/components/classic/download/
- 注意:jdk8最高只支持5.15.x,下载更高版本的需要对应的jdk环境
- 下载可以参考其他的安装教程,网上一搜一大把
- 安装成功之后访问ActiveMQ网址:http://localhost:8161/admin/
- 初始账号密码都是admin,登陆进去之后就说明安装成功了。
-
页面存在Queues 和 Topics 这两个选项,这两个选项分别是点对点消息和发布/订阅消息的查看窗⼝。
- 点对点消息:消息⽣产者⽣产消息发布到 queue 中,然后消息消费者从 queue 中取出,并且消费消息。这⾥需要注意:消息被消费者消费
以后,queue 中不再有存储,所以消息消费者不可消费到已经被消费的消息。Queue ⽀持存在多个消息消费者,但是对⼀个消息⽽⾔,只
会有⼀个消费者可以消费。 - 发布/订阅消息:消息⽣产者(发布)将消息发布到 topic 中,同时有多个消息消费者(订阅)消费该消息。和点对点⽅式不同,发布到
topic 的消息会被所有订阅者消费。下⾯分析具体的实现⽅式。
- 点对点消息:消息⽣产者⽣产消息发布到 queue 中,然后消息消费者从 queue 中取出,并且消费消息。这⾥需要注意:消息被消费者消费
集成ActiveMQ
依赖导入与配置
- 依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
- 配置
- 在application.yml文件中增加ActiveMQ的配置
spring:
activemq:
# activemq url
broker-url: tcp://localhost:61616
in-memory: true
pool:
# 如果此处设置为true,需要添加activemq-pool的依赖包,否则会⾃动配置失败,⽆法注⼊JmsMessagingTemplate
enabled: false
Queue 和 Topic 创建
- 创建两种消息 Queue 和 Topic,这两种消息的创建,我们放到 ActiveMqConfig 中来创建
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTopic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.jms.Destination;
/**
* @author liujf
*/
@Configuration
public class ActiveMqConfig {
public static final String TOPIC_NAME = "activemq.topic";
public static final String QUEUE_NAME = "activemq.queue";
@Bean
public Destination topic() {
return new ActiveMQTopic(TOPIC_NAME);
}
@Bean
public Destination queue() {
return new ActiveMQQueue(QUEUE_NAME);
}
}
- 可以看出创建 Queue 和 Topic 两种消息,分别使⽤ new ActiveMQQueue 和 new ActiveMQTopic 来创建,分别跟上对应消息的名称即
可。这样在其他地⽅就可以直接将这两种消息作为组件注⼊进来了。
发送消息
- 在 Spring Boot 中,我们只要注⼊ JmsMessagingTemplate 模板即可快速发送消息
@Service
public class MsgProducer {
@Resource
private JmsMessagingTemplate jmsMessagingTemplate;
public void sendMessage(Destination destination, String msg){
jmsMessagingTemplate.convertAndSend(destination.getName(), msg);
}
}
- convertAndSend ⽅法中第⼀个参数是消息发送的⽬的地,第⼆个参数是具体的消息内容。
点对点消息生产与消费
- 消息的生产一般在Controller中,由于上面生成了Queue消息的组件,所以在Controller中我们直接注入就可以,然后调用上文的消息发送方法sendMessage就可以生产一条消息了。
@RestController
@RequestMapping("/activemq")
public class ActiveMqController {
private static final Logger logger = LoggerFactory.getLogger(ActiveMqController.class);
@Resource
private MsgProducer msgProducer;
@Resource
private Destination queue;
@GetMapping("/send/queue")
public String sendQueueMessage(){
logger.info("开始发送点对点的消息-------------");
msgProducer.sendMessage(queue,"queue:hello activemq!");
return "success";
}
}
- 消息的消费很简单,我们只需指定目的地就可以了,jms监听器一直在监听是否有消息过来,如果有则消费。
@Service
public class QueueConsumer {
@JmsListener(destination = ActiveMqConfig.QUEUE_NAME)
public void receiveQueueMsg(String msg){
System.out.println("收到消息"+msg);
}
}
- 消费者只需要定义业务类就行了,不需要主动的去消费,当监听到有消息后就会自动的消费。
- 访问:http://localhost:8001/activemq/send/queue
- 控制台打印:=开始发送点对点消息=
- 收到消息Queue: hello activemq!
发布/订阅消息的生产和消费
- 消息的生产和点对点的形式一样,我们注入Topic并调用producer的sendMessage方法即可发送订阅消息
- 定义生产逻辑
@Service
public class MsgProducer {
@Resource
private JmsMessagingTemplate jmsMessagingTemplate;
public void sendMessage(Destination destination, String msg){
jmsMessagingTemplate.convertAndSend(destination, msg);
}
}
- controller中进行测试
@RestController
@RequestMapping("/activemq")
public class ActiveTopicController {
private static final Logger logger = LoggerFactory.getLogger(ActiveMqController.class);
@Resource
private MsgProducer producer;
@Resource
private Destination topic;
@GetMapping("/send/topic")
public String sendTopicMessage() {
logger.info("===开始发送订阅消息===");
producer.sendMessage(topic, "Topic: hello activemq!");
return "success";
}
}
- 发布/订阅消息的消息和点对点不同,订阅消息支持多个消费者一起消费。其次,SpringBoot中默认的点对点消息,所以在使用Topic时会不起作用。
- 我们需要在配置⽂件 application.yml 中添加⼀个配置:
spring:
jms:
pub-sub-domain: true
- 该配置是 false 的话,则为点对点消息,也是 Spring Boot 默认的。这样是可以解决问题,但是如果这样配置的话,上⾯提到的点对点消
息⼜不能正常消费了。所以⼆者不可兼得,这并⾮⼀个好的解决办法。 - ⽐较好的解决办法是,我们定义⼀个⼯⼚,@JmsListener 注解默认只接收 queue 消息,如果要接收 topic 消息,需要设置⼀下
containerFactory。我们还在上⾯的那个 ActiveMqConfig 配置类中添加:
@Configuration
public class ActiveMqConfig {
public static final String TOPIC_NAME = "activemq.topic";
public static final String QUEUE_NAME = "activemq.queue";
@Bean
public Destination topic() {
return new ActiveMQTopic(TOPIC_NAME);
}
@Bean
public Destination queue() {
return new ActiveMQQueue(QUEUE_NAME);
}
/**
* JmsListener注解默认只接收queue消息,如果要接收topic消息,需要设置containerFactory
*/
@Bean
public JmsListenerContainerFactory topicListenerContainer(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
// 相当于在application.yml中配置:spring.jms.pub-sub-domain=true
factory.setPubSubDomain(true);
return factory;
}
}
- 经过这样的配置之后,我们在消费的时候,在 @JmsListener 注解中指定这个容器⼯⼚即可消费 topic 消息。
- 定义多个消费者业务类:
@Service
public class TopicConsumer {
/**
* 接收订阅消息
* @param msg
*/
@JmsListener(destination = ActiveMqConfig.TOPIC_NAME, containerFactory = "topicListenerContainer")
public void receiveTopicMsg(String msg) {
System.out.println("消费者1号收到的消息为:" + msg);
}
}
@Service
public class TopicConsumer2 {
/**
* 接收订阅消息
* @param msg
*/
@JmsListener(destination = ActiveMqConfig.TOPIC_NAME, containerFactory = "topicListenerContainer")
public void receiveTopicMsg(String msg) {
System.out.println("消费者2号收到的消息为:" + msg);
}
}
- 访问:http://localhost:8001/activemq/send/topic
- 控制台打印:
- =开始发送订阅消息=
- 消费者2号收到的消息为:Topic: hello activemq!
- 消费者1号收到的消息为:Topic: hello activemq!
- 控制台打印: