第 4-5 课:使⽤ Spring Boot 操作 ActiveMQ

消息队列中间件是分布式系统中重要的组件,主要解决应⽤耦合、异步消息、流量削锋等问题,实现⾼性
能、⾼可⽤、可伸缩和最终⼀致性架构,是⼤型分布式系统不可缺少的中间件。
 
⽬前在⽣产环境中使⽤较多的消息队列有 ActiveMQ RabbitMQ ZeroMQ Kafka MetaMQ RocketMQ
等。
特性
  • 异步性:将耗时的同步操作通过以发送消息的⽅式进⾏了异步化处理,减少了同步等待的时间。
  • 松耦合:消息队列减少了服务之间的耦合性,不同的服务可以通过消息队列进⾏通信,⽽不⽤关⼼彼此 的实现细节,只要定义好消息的格式就⾏。
  • 分布式:通过对消费者的横向扩展,降低了消息队列阻塞的⻛险,以及单个消费者产⽣单点故障的可能 性(当然消息队列本身也可以做成分布式集群)。
  • 可靠性:消息队列⼀般会把接收到的消息存储到本地硬盘上(当消息被处理完之后,存储信息根据不同 的消息队列实现,有可能将其删除),这样即使应⽤挂掉或者消息队列本身挂掉,消息也能够重新加 载。

JMS 规范

JMS Java 消息服务( Java Message Service )应⽤程序接⼝,是⼀个 Java 平台中关于⾯向消息中间件
MOM )的 API ,⽤于在两个应⽤程序之间,或分布式系统中发送消息,进⾏异步通信。 Java 消息服务是
⼀个与具体平台⽆关的 API ,绝⼤多数 MOM 提供商都对 JMS 提供⽀持。
 
JMS 的消息机制有 2 种模型,⼀种是 Point to Point ,表现为队列的形式,发送的消息,只能被⼀个接收者取
⾛;另⼀种是 Topic ,可以被多个订阅者订阅,类似于群发。
 
ActiveMQ JMS 的⼀个实现。

ActiveMQ 介绍

ActiveMQ Apache 软件基⾦下的⼀个开源软件,它遵循 JMS1.1 规范( Java Message Service ),是消息
驱动中间件软件( MOM )。它为企业消息传递提供⾼可⽤、出⾊性能、可扩展、稳定和安全保障。
ActiveMQ 使⽤ Apache 许可协议,因此,任何⼈都可以使⽤和修改它⽽不必反馈任何改变。
 
ActiveMQ 的⽬标是在尽可能多的平台和语⾔上提供⼀个标准的,消息驱动的应⽤集成。 ActiveMQ 实现 JMS
规范并在此之上提供⼤量额外的特性。 ActiveMQ ⽀持队列和订阅两种模式的消息发送。
 
Spring Boot 提供了 ActiveMQ 组件 spring-boot-starter-activemq ,⽤来⽀持 ActiveMQ Spring Boot 体系
内使⽤,下⾯我们来详细了解如何使⽤。

添加依赖

主要添加组件: spring-boot-starter-activemq
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

配置⽂件

application.properties 中添加配置。
# 基于内存的 ActiveMQ
spring.activemq.in-memory=true
# 不适应连接池
spring.activemq.pool.enabled=false
# 独⽴安装的 ActiveMQ
#spring.activemq.broker-url=tcp://192.168.0.1:61616
#spring.activemq.user=admin
#spring.activemq.password=admin
在使⽤ ActiveMQ 时有两种使⽤⽅式,⼀种是使⽤独⽴安装的 ActiveMQ ,在⽣产环境推荐使⽤这种;另⼀种
是使⽤基于内存 ActiveMQ ,在调试阶段建议使⽤这种⽅式。

队列(Queue

队列发送的消息,只能被⼀个消费者接收。

创建队列

@Configuration
public class MqConfig {
 @Bean
 public Queue queue() {
 return new ActiveMQQueue("neo.queue");
 }
}
使⽤ @Confifiguration 注解在项⽬启动时,定义了⼀个队列 queue 命名为: neo.queue

消息⽣产者

创建⼀个消息的⽣产者:
@Component
public class Producer{
 @Autowired
 private JmsMessagingTemplate jmsMessagingTemplate;
 @Autowired
 private Queue queue;
 public void sendQueue(String msg) {
 System.out.println("send queue msg :"+msg);
 this.jmsMessagingTemplate.convertAndSend(this.queue, msg);
 }
}
JmsMessagingTemplate Spring 提供发送消息的⼯具类,使⽤ JmsMessagingTemplate 和创建好的
queue 对消息进⾏发送。

消息消费者

@Component
public class Consumer {
 @JmsListener(destination = "neo.queue")
 public void receiveQueue(String text) {
 System.out.println("Consumer queue msg : "+text);
 }
}
使⽤注解 @JmsListener(destination = "neo.queue") ,表示此⽅法监控了名为 neo.queue 的队列。当队列
neo.queue 中有消息发送时会触发此⽅法的执⾏, text 为消息内容。

测试

创建 SampleActiveMqTests 测试类,注⼊创建好的消息⽣产者。
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleActiveMqTests {
 @Autowired
 private Producer producer;
 @Rule
 public OutputCapture outputCapture = new OutputCapture();
}
OutputCapture Spring Boot 提供的⼀个测试类,它能捕获 System.out System.err 的输出,我们可以利
⽤这个特性来判断程序中的输出是否执⾏。
 
@Test
public void sendSimpleQueueMessage() throws InterruptedException {
 this.producer.sendQueue("Test queue message");
 Thread.sleep(1000L);
 assertThat(this.outputCapture.toString().contains("Test queue")).isTrue();
}
创建测试⽅式,使⽤ producer 发送消息,为了保证容器可以接收到消息,让测试⽅法等待 1 秒,最后使⽤
outputCapture 判断是否执⾏成功。

测试多消费者

上⾯的案例只是⼀个⽣产者⼀个消费者,我们在模拟⼀个⽣产者和多个消费者队列的执⾏情况。我们复制上
⾯的消费者 Consumer 重新命名为 Consumer2 ,并且将输出内容加上 2 的关键字,如下:
@Component
public class Consumer2 {
 @JmsListener(destination = "neo.queue")
 public void receiveQueue(String text) {
 System.out.println("Consumer2 queue msg : "+text);
 }
}
在刚才的测试类中添加⼀个 send100QueueMessage() ⽅法,模式发送 100 条消息时,两个消费者是如何消
费消息的。
 
@Test
public void send100QueueMessage() throws InterruptedException {
 for (int i=0;i<100;i++){
 this.producer.sendQueue("Test queue message"+i);
 }
 Thread.sleep(1000L);
}
控制台输出结果:
Consumer queue msg : Test queue message0
Consumer2 queue msg : Test queue message1
Consumer queue msg : Test queue message2
Consumer2 queue msg : Test queue message3
...
根据控制台输出的消息可以看出,当有多个消费者监听⼀个队列时,消费者会⾃动均衡负载的接收消息,并
且每个消息只能有⼀个消费者所接收。 GitChat
 
注意:控制台输出 javax.jms.JMSException: peer (vm://localhost#1) stopped. 报错信息可以忽略,这
Info 级别的错误,是 ActiveMQ 的⼀个 bug

⼴播(Topic

⼴播发送的消息,可以被多个消费者接收。

创建 Topic

@Configuration
public class MqConfig {
 @Bean
 public Topic topic() {
 return new ActiveMQTopic("neo.topic");
 }
}
使⽤ @Confifiguration 注解在项⽬启动时,定义了⼀个⼴播 Topic 命名为: neo.topic

消息⽣产者

创建⼀个消息的⽣产者:
@Component
public class Producer{
 @Autowired
 private JmsMessagingTemplate jmsMessagingTemplate;
 @Autowired
 private Topic topic;
 public void sendTopic(String msg) {
 System.out.println("send topic msg :"+msg);
 this.jmsMessagingTemplate.convertAndSend(this.topic, msg);
 }
}
和上⾯的⽣产者对⽐只是 convertAndSend() ⽅法传⼊的第⼀个参数变成了 Topic

消息消费者

@Component
public class Consumer {
 @JmsListener(destination = "neo.topic")
 public void receiveTopic(String text) {
 System.out.println("Consumer topic msg : "+text);
 }
}
消费者也没有变化,只是监听的名改为上⾯的 neo.topic ,因为模拟多个消费者,复制⼀份 Consumer 命名为
Consumer2 ,代码相同在输出中标明来⾃ Consumer2

测试

创建 SampleActiveMqTests 测试类,注⼊创建好的消息⽣产者。
@Test
public void sendSimpleTopicMessage() throws InterruptedException {
 this.producer.sendTopic("Test Topic message");
 Thread.sleep(1000L);
}
测试⽅法执⾏成功后,会看到控制台输出信息,如下:
send topic msg :Test Topic message
Consumer topic msg : Test Topic message
Consumer2 topic msg : Test Topic message
可以看出两个消费者都收到了发送的消息,从⽽验证⼴播( Topic )是⼀个发送者多个消费者的模式。
同时⽀持队列( Queue )和⼴播( Topic
 
Spring Boot 集成 ActiveMQ 的项⽬默认只⽀持队列或者⼴播中的⼀种,通过配置项 spring.jms.pub-sub
domain 的值来控制, true 为⼴播模式, false 为队列模式,默认情况下⽀持队列模式。
如果需要在同⼀项⽬中既⽀持队列模式也⽀持⼴播模式,可以通过 DefaultJmsListenerContainerFactory
建⾃定义的 JmsListenerContainerFactory 实例,之后在 @JmsListener 注解中通过 containerFactory 属性引
⽤它。
 
分别创建两个⾃定义的 JmsListenerContainerFactory 实例,通过 pubSubDomain 来控制是⽀持队列模式还
是⼴播模式。 GitChat
 
@Configuration
@EnableJms
public class ActiveMQConfig {
 @Bean("queueListenerFactory")
 public JmsListenerContainerFactory<?> queueListenerFactory(ConnectionFactory c
onnectionFactory) {
 DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContain
erFactory();
 factory.setConnectionFactory(connectionFactory);
 factory.setPubSubDomain(false);
 return factory;
 }
 @Bean("topicListenerFactory")
 public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory c
onnectionFactory) {
 DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContain
erFactory();
 factory.setConnectionFactory(connectionFactory);
 factory.setPubSubDomain(true);
 return factory;
 }
}
然后在消费者接收的⽅法中,指明使⽤ containerFactory 接收消息。
@Component
public class Consumer {
 @JmsListener(destination = "neo.queue", containerFactory = "queueListenerFacto
ry")
 public void receiveQueue(String text) {
 System.out.println("Consumer queue msg : "+text);
 }
 @JmsListener(destination = "neo.topic", containerFactory = "topicListenerFacto
ry")
 public void receiveTopic(String text) {
 System.out.println("Consumer topic msg : "+text);
 }
}
改造完成之后,再次执⾏队列和⼴播的测试⽅法,就会发现项⽬同时⽀持了两种类型的消息收发。

总结

消息中间件⼴泛应⽤在⼤型互联⽹架构中,利⽤消息中间件队列和⼴播各⾃的特性可以⽀持很多业务,⽐如
群发发送短信、给单个⽤户发送邮件等。 ActiveMQ 是⼀款⾮常流⾏的消息中间件,它的特点是部署简单、使
⽤⽅便,⽐较适合中⼩型团队。 Spring Boot 提供了集成 ActiveMQ 对应的组件,在 Spring Boot 中使⽤
ActiveMQ 只需要添加相关注解即可。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值