目前随着微服务以及分布式的发展,消息中间件的作用越来越大了。消息中间件可以起到系统之间解耦的作用,比如用户进行购买商品,有加入购物车操作,还有下单操作以及支付操作等。在微服务的情况下,这些将是不同的服务,有可能部署在不同的容器中,因此,这个购买操作存在各个系统之间的调用关系。其中支付之后会有支付信息,以及物流等信息反馈给用户。但是如果在一个串行调用的系统中则是非常不合理的。对于用户来说支付信息和物流信息等延迟几秒基本是无感的。因此,这里会使用消息中间件,这里支付系统会生产一个支付结果的消息,然后结果反馈(短信、应用程序通知)系统和物流系统则会订阅这个消息。支付系统支付完成之后则会处理下一个订单,不在关心该订单的后续情况,结果反馈系统和物流系统则会监听到这个支付结果,并进行该订单的后续处理。从上可以看出,消息中间件解耦了支付系统和结果反馈系统以及物流系统。常见的消息中间件有:kafka、ActiveMQ、RabbitMQ。消息中间件主要有以下特点:
(1).异步处理
(2).应用解耦
(3).流量削峰
本文通过介绍Springboot整合ActiveMQ的项目示例来介绍消息中间件的作用,以及ActiveMQ的特性。
ActiveMQ中消息的两种角色:消息生产者和消息消费者。简而言之,消息生产者负责发送消息,而消息消费者则会处理消息。
ActiveMQ具有两种模式:点对点模式(Queue)和发布订阅模式(Topic)。其中点对点模式是指某一条产生的消息只能被一个消息消费者消费,即消息与消费者是一对一的关系。而发布订阅模式则有一个Topic,可以被所有监听它的消费者消费。
1.pom文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.6.RELEASE
com.carson
activemq-producer
0.0.1-SNAPSHOT
activemq-producer
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-activemq
org.messaginghub
pooled-jms
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework
spring-web
5.2.0.RELEASE
org.springframework.boot
spring-boot-test
2.2.0.RELEASE
test
org.springframework.boot
spring-boot-maven-plugin
springboot整合activemq的依赖如下所示(springboot版本为2.1及其以上)
org.springframework.boot
spring-boot-starter-activemq
org.messaginghub
pooled-jms
2.设置一个常量类,Constant
public class Constant {
public static final String QUEUEMESSAGE="queue";
public static final String TOPICMESSAGE="topic";
}
3.消息生产类Producer
@RestController
public class Producer {
@Autowired
private JmsMessagingTemplate jmsMessageingTemplate;
@Autowired
private Queue queue;
@Autowired
private Topic topic;
@RequestMapping("/sendMessage/{msg}")
public void sendMessage(@PathVariable("msg") String msg){
this.jmsMessageingTemplate.convertAndSend(queue,msg);
}
@RequestMapping("/sendMessage1/{msg}")
public void sendMessage1(@PathVariable("msg") String msg){
this.jmsMessageingTemplate.convertAndSend(topic,msg);
}
}
其中sendMessage方法是用于验证点对点的形式,sendMessage1用于验证topic的形式。
4.消息消费者Consumer、Consumer1
@Component
public class Consumer {
@JmsListener(destination= Constant.QUEUEMESSAGE)
public void receiveQueue(String message) throws JMSException {
if(null!=message){
System.out.println("我是消费者一号,接收到的queue是:"+message);
}else{
System.out.print("我是消费者一号,我接收到空queue");
}
}
@JmsListener(destination= Constant.TOPICMESSAGE)
public void receiveQueue1(String message) throws JMSException {
if(null!=message){
System.out.println("我是消费者一号,接收到的topic是:"+message);
}else{
System.out.print("我是消费者一号,我接收到空topic");
}
}
}
@Component
public class Consumer1 {
@JmsListener(destination= Constant.QUEUEMESSAGE)
public void receiveQueue(String message) throws JMSException {
if(null!=message){
System.out.println("我是消费者二号,接收到的queue是:"+message);
}else{
System.out.print("我是消费者二号,我接收到空queue");
}
}
@JmsListener(destination= Constant.TOPICMESSAGE)
public void receiveQueue1(String message) throws JMSException {
if(null!=message){
System.out.println("我是消费者二号,接收到的topic是:"+message);
}else{
System.out.print("我是消费者二号,我接收到空topic");
}
}
}
@Component注解表示该类作为组件注入到容器中。
5.application.properties
server.port=8089
spring.activemq.broker-url=tcp://127.0.0.1:61616
# 在考虑结束之前等待的时间
#spring.activemq.close-timeout=15s
# 默认代理URL是否应该在内存中。如果指定了显式代理,则忽略此值。
spring.activemq.in-memory=true
# 是否在回滚回滚消息之前停止消息传递。这意味着当启用此命令时,消息顺序不会被保留。
spring.activemq.non-blocking-redelivery=false
# 等待消息发送响应的时间。设置为0等待永远。
#spring.activemq.send-timeout=1
#默认情况下activemq提供的是queue模式,若要使用topic模式需要配置下面配置
#账号
spring.activemq.user=admin
# 密码
spring.activemq.password=admin
6.启动类
@SpringBootApplication
public class ActivemqApplication {
@Bean
public Queue queue(){
return new ActiveMQQueue(Constant.QUEUEMESSAGE);
}
@Bean
public Topic topic(){
return new ActiveMQTopic(Constant.TOPICMESSAGE);
}
public static void main(String[] args) {
SpringApplication.run(ActivemqApplication.class, args);
}
}
queue()、topic()方法分别表示注册点对点的queue和topic。
7.启动项目
结果如下:
我是消费者一号,接收到的queue是:12345
说明queue模式生产的消息只会对应一个消费者。
server.port=8089
spring.activemq.broker-url=tcp://127.0.0.1:61616
# 在考虑结束之前等待的时间
#spring.activemq.close-timeout=15s
# 默认代理URL是否应该在内存中。如果指定了显式代理,则忽略此值。
spring.activemq.in-memory=true
# 是否在回滚回滚消息之前停止消息传递。这意味着当启用此命令时,消息顺序不会被保留。
spring.activemq.non-blocking-redelivery=false
# 等待消息发送响应的时间。设置为0等待永远。
#spring.activemq.send-timeout=1
#默认情况下activemq提供的是queue模式,若要使用topic模式需要配置下面配置
spring.jms.pub-sub-domain=true
#账号
spring.activemq.user=admin
# 密码
spring.activemq.password=admin
发布订阅模式只需要在原有基础上将application.properties修改为如上所示,
spring.jms.pub-sub-domain=true表示开启了topic模式。
启动项目,并用postman调用http://127.0.0.1:8089/sendMessage1/123,结果如下:
我是消费者一号,接收到的topic是:123
我是消费者二号,接收到的topic是:123
运行结果说明,两个消费者都消费topic。 说明topic模式表示一个消费对应所有的消费者。
以上说明了activemq的queue和topic两种模式,其中queue模式可以适用于秒杀项目,topic可以用于多个系统监听一个消息的场景。