Spring Boot 入门之消息中间件篇(五)
一、前言
在消息中间件中有 2 个重要的概念:消息代理和目的地。当消息发送者发送消息后,消息就被消息代理接管,消息代理保证消息传递到指定目的地。
我们常用的消息代理有 JMS 和 AMQP 规范。对应地,它们常见的实现分别是 ActiveMQ 和 RabbitMQ。
上篇文章《Spring Boot 入门之缓存和 NoSQL 篇(四)》。
二、整合 ActiveMQ
2.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!-- 如果需要配置连接池,添加如下依赖 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
</dependency>
2.2 添加配置
# activemq 配置
spring.activemq.broker-url=tcp://192.168.2.12:61616
spring.activemq.user=admin
spring.activemq.password=admin
spring.activemq.pool.enabled=false
spring.activemq.pool.max-connections=50
# 使用发布/订阅模式时,下边配置需要设置成 true
spring.jms.pub-sub-domain=false
此处 spring.activemq.pool.enabled=false,表示关闭连接池。
2.3 编码
配置类:
@Configuration
public class JmsConfirguration {
public static final String QUEUE_NAME = "activemq_queue";
public static final String TOPIC_NAME = "activemq_topic";
@Bean
public Queue queue() {
return new ActiveMQQueue(QUEUE_NAME);
}
@Bean
public Topic topic() {
return new ActiveMQTopic(TOPIC_NAME);
}
}
负责创建队列和主题。
消息生产者:
@Component
public class JmsSender {
@Autowired
private Queue queue;
@Autowired
private Topic topic;
@Autowired
private JmsMessagingTemplate jmsTemplate;
public void sendByQueue(String message) {
this.jmsTemplate.convertAndSend(queue, message);
}
public void sendByTopic(String message) {
this.jmsTemplate.convertAndSend(topic, message);
}
}
消息消费者:
@Component
public class JmsReceiver {
@JmsListener(destination = JmsConfirguration.QUEUE_NAME)
public void receiveByQueue(String message) {
System.out.println("接收队列消息:" + message);
}
@JmsListener(destination = JmsConfirguration.TOPIC_NAME)
public void receiveByTopic(String message) {
System.out.println("接收主题消息:" + message);
}
}
消息消费者使用 @JmsListener 注解监听消息。
2.4 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class JmsTest {
@Autowired
private JmsSender sender;
@Test
public void testSendByQueue() {
for (int i = 1; i < 6; i++) {
this.sender.sendByQueue("hello activemq queue " + i);
}
}
@Test
public void testSendByTopic() {
for (int i = 1; i < 6; i++) {
this.sender.sendByTopic("hello activemq topic " + i);
}
}
}
打印结果:
接收队列消息:hello activemq queue 1
接收队列消息:hello activemq queue 2
接收队列消息:hello activemq queue 3
接收队列消息:hello activemq queue 4
接收队列消息:hello activemq queue 5
测试发布/订阅模式时,设置 spring.jms.pub-sub-domain=true
接收主题消息:hello activemq topic 1
接收主题消息:hello activemq topic 2
接收主题消息:hello activemq topic 3
接收主题消息:hello activemq topic 4
接收主题消息:hello activemq topic 5
三、整合 RabbitMQ
3.1 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3.2 添加配置
spring.rabbitmq.host=192.168.2.30
spring.rabbitmq.port=5672
spring.rabbitmq.username=light
spring.rabbitmq.password=light
spring.rabbitmq.virtual-host=/test
3.3 编码
配置类:
@Configuration
public class AmqpConfirguration {
//=============简单、工作队列模式===============
public static final String SIMPLE_QUEUE = "simple_queue";
@Bean
public Queue queue() {
return new Queue(SIMPLE_QUEUE, true);
}
//===============发布/订阅模式============
public static final String PS_QUEUE_1 = "ps_queue_1";
public static final String PS_QUEUE_2 = "ps_queue_2";
public static final String FANOUT_EXCHANGE = "fanout_exchange";
@Bean
public Queue psQueue1() {
return new Queue(PS_QUEUE_1, true);
}
@Bean
public Queue psQueue2() {
return new Queue(PS_QUEUE_2, true);
}
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange(FANOUT_EXCHANGE);
}
@Bean
public Binding fanoutBinding1() {
return BindingBuilder.bind(psQueue1()).to(fanoutExchange());
}
@Bean
public Binding fanoutBinding2() {
return BindingBuilder.bind(psQueue2()).to(fanoutExchange());
}
//===============路由模式============
public static final String ROUTING_QUEUE_1 = "routing_queue_1";
public static final String ROUTING_QUEUE_2 = "routing_queue_2";
public static final String DIRECT_EXCHANGE = "direct_exchange";
@Bean
public Queue routingQueue1() {
return new Queue(ROUTING_QUEUE_1, true);
}
@Bean
public Queue routingQueue2() {
return new Queue(ROUTING_QUEUE_2, true);
}
@Bean
public DirectExchange directExchange() {
return new DirectExchange(DIRECT_EXCHANGE);
}
@Bean
public Binding directBinding1() {
return BindingBuilder.bind(routingQueue1()).to(directExchange()).with("user");
}
@Bean
public Binding directBinding2() {
return BindingBuilder.bind(routingQueue2()).to(directExchange()).with("order");
}
//===============主题模式============
public static final String TOPIC_QUEUE_1 = "topic_queue_1";
public static final String TOPIC_QUEUE_2 = "topic_queue_2";
public static final String TOPIC_EXCHANGE = "topic_exchange";
@Bean
public Queue topicQueue1() {
return new Queue(TOPIC_QUEUE_1, true);
}
@Bean
public Queue topicQueue2() {
return new Queue(TOPIC_QUEUE_2, true);
}
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(TOPIC_EXCHANGE);
}
@Bean
public Binding topicBinding1() {
return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("user.add");
}
@Bean
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("user.#");
}
}
RabbitMQ 有多种工作模式,因此配置比较多。想了解相关内容的读者可以查看本站的《RabbitMQ 工作模式介绍》或者自行百度相关资料。
消息生产者:
@Component
public class AmqpSender {
@Autowired
private AmqpTemplate amqpTemplate;
/**
* 简单模式发送
*
* @param message
*/
public void simpleSend(String message) {
this.amqpTemplate.convertAndSend(AmqpConfirguration.SIMPLE_QUEUE, message);
}
/**
* 发布/订阅模式发送
*
* @param message
*/
public void psSend(String message) {
this.amqpTemplate.convertAndSend(AmqpConfirguration.FANOUT_EXCHANGE, "", message);
}
/**
* 路由模式发送
*
* @param message
*/
public void routingSend(String routingKey, String message) {
this.amqpTemplate.convertAndSend(AmqpConfirguration.DIRECT_EXCHANGE, routingKey, message);
}
/**
* 主题模式发送
*
* @param routingKey
* @param message
*/
public void topicSend(String routingKey, String message) {
this.amqpTemplate.convertAndSend(AmqpConfirguration.TOPIC_EXCHANGE, routingKey, message);
}
}
消息消费者:
@Component
public class AmqpReceiver {
/**
* 简单模式接收
*
* @param message
*/
@RabbitListener(queues = AmqpConfirguration.SIMPLE_QUEUE)
public void simpleReceive(String message) {
System.out.println("接收消息:" + message);
}
/**
* 发布/订阅模式接收
*
* @param message
*/
@RabbitListener(queues = AmqpConfirguration.PS_QUEUE_1)
public void psReceive1(String message) {
System.out.println(AmqpConfirguration.PS_QUEUE_1 + "接收消息:" + message);
}
@RabbitListener(queues = AmqpConfirguration.PS_QUEUE_2)
public void psReceive2(String message) {
System.out.println(AmqpConfirguration.PS_QUEUE_2 + "接收消息:" + message);
}
/**
* 路由模式接收
*
* @param message
*/
@RabbitListener(queues = AmqpConfirguration.ROUTING_QUEUE_1)
public void routingReceive1(String message) {
System.out.println(AmqpConfirguration.ROUTING_QUEUE_1 + "接收消息:" + message);
}
@RabbitListener(queues = AmqpConfirguration.ROUTING_QUEUE_2)
public void routingReceive2(String message) {
System.out.println(AmqpConfirguration.ROUTING_QUEUE_2 + "接收消息:" + message);
}
/**
* 主题模式接收
*
* @param message
*/
@RabbitListener(queues = AmqpConfirguration.TOPIC_QUEUE_1)
public void topicReceive1(String message) {
System.out.println(AmqpConfirguration.TOPIC_QUEUE_1 + "接收消息:" + message);
}
@RabbitListener(queues = AmqpConfirguration.TOPIC_QUEUE_2)
public void topicReceive2(String message) {
System.out.println(AmqpConfirguration.TOPIC_QUEUE_2 + "接收消息:" + message);
}
}
消息消费者使用 @RabbitListener 注解监听消息。
3.4 测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class AmqpTest {
@Autowired
private AmqpSender sender;
@Test
public void testSimpleSend() {
for (int i = 1; i < 6; i++) {
this.sender.simpleSend("test simpleSend " + i);
}
}
@Test
public void testPsSend() {
for (int i = 1; i < 6; i++) {
this.sender.psSend("test psSend " + i);
}
}
@Test
public void testRoutingSend() {
for (int i = 1; i < 6; i++) {
this.sender.routingSend("order", "test routingSend " + i);
}
}
@Test
public void testTopicSend() {
for (int i = 1; i < 6; i++) {
this.sender.topicSend("user.add", "test topicSend " + i);
}
}
}
测试结果略过。。。
踩坑提醒1:ACCESS_REFUSED - Login was refused using authentication mechanism PLAIN
解决方案:
请确保用户名和密码是否正确,需要注意的是用户名和密码的值是否包含空格或制表符(笔者测试时就是因为密码多了一个制表符导致认证失败)。
如果测试账户使用的是 guest,需要修改 rabbitmq.conf 文件。在该文件中添加 “loopback_users = none” 配置。
踩坑提醒2:Cannot prepare queue for listener. Either the queue doesn't exist or the broker will not allow us to use it
解决方案:
我们可以登陆 RabbitMQ 的管理界面,在 Queue 选项中手动添加对应的队列。
四、源码下载
五、参考资料
Spring Boot 入门之消息中间件篇(五)的更多相关文章
- Spring Boot 入门之消息中间件篇(转发)
一.前言 在消息中间件中有 2 个重要的概念:消息代理和目的地.当消息发送者发送消息后,消息就被消息代理接管,消息代理保证消息传递到指定目的地. 我们常用的消息代理有 JMS 和 AMQP 规范.对应 ...
- Spring Boot 入门之基础篇(一)
原文地址:Spring Boot 入门之基础篇(一) 博客地址:http://www.extlight.com 一.前言 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是 ...
- Spring Boot 入门之 Web 篇(二)
原文地址:Spring Boot 入门之 Web 篇(二) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之基础篇(一)>介绍了 ...
- Spring Boot 入门之单元测试篇(五)
博客地址:http://www.moonxy.com 一.前言 JUnit 是一个由 Java 语言编写的开源的回归测试(回归测试是指重复以前全部或部分的相同测试)框架,由Erich Gamma 和 ...
- Spring Boot 入门之 Cache 篇(四)
博客地址:http://www.moonxy.com 一.前言 Spring Cache 对 Cahce 进行了抽象,提供了 @Cacheable.@CachePut.@CacheEvict 等注解. ...
- Spring Boot入门系列(十五)Spring Boot 开发环境热部署
在实际的项目开发过中,当我们修改了某个java类文件时,需要手动重新编译.然后重新启动程序的,整个过程比较麻烦,特别是项目启动慢的时候,更是影响开发效率.其实Spring Boot的项目碰到这种情况, ...
- Spring Boot 入门之持久层篇(三)
原文地址:Spring Boot 入门之持久层篇(三) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之 Web 篇(二)>介绍了 ...
- spring boot入门教程——Spring Boot快速入门指南
Spring Boot已成为当今最流行的微服务开发框架,本文是如何使用Spring Boot快速开始Web微服务开发的指南,我们将使创建一个可运行的包含内嵌Web容器(默认使用的是Tomcat)的可运 ...
- Spring Cloud 入门 之 Zuul 篇(五)
原文地址:Spring Cloud 入门 之 Zuul 篇(五) 博客地址:http://www.extlight.com 一.前言 随着业务的扩展,微服务会不对增加,相应的其对外开放的 API 接口 ...
随机推荐
- loj2163 / bzoj2212 / P3521 [POI2011]ROT-Tree Rotations(线段树合并)
P3521 [POI2011]ROT-Tree Rotations loj2163 [POI2011]ROT-Tree Rotations(数据加强) (loj的数据套了个fread优化才过...) ...
- Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句 # python3 # author lizm # datetime 2018-02-01 10:00:00 # -*- co ...
- HeyWeGo第三周项目总结
HeyWeGo第三周项目总结 项目进展 第三周的计划是:将收集到的资料和代码,进行汇总并且分工后开始开始撰写游戏代码与测试代码. 本周我们已经开始编写了自己负责部分的代码. 按照我们的计划,本周我们完 ...
- crontab 定时执行脚本出错,但手动执行脚本正常
原因: crontab 没有去读环境变量,需要再脚本中手动引入环境变量,可以用source 也可以用export 写死环境变量. 为了定时监控Linux系统CPU.内存.负载的使用情况,写了个Shel ...
- git squash 和 git rebase
In git, what is the difference between merge --squash and rebase? 上面链接的回答中的总结: Both git merge --squa ...
- UVa 11582 巨大的斐波那契数!(幂取模)
https://vjudge.net/problem/UVA-11582 题意: 输入两个非负整数a.b和正整数n,你的任务是计算f(a^b)除以n的余数.f[0]=0,f[1]=1,f[i+2]=f ...
- SpringBoot.资料
1.du 搜 "springboot 视频" 2.SpringBoot视频教程_哔哩哔哩 (゜-゜)つロ 干杯_-bilibili.html(https://www.bilibil ...
- Angular i18n
Angular2中使用ngx-translate进行国际化http://blog.csdn.net/u014291497/article/details/61233033 在 Angular 项目中添 ...
- 浅谈Android Studio3.0更新之路(遇坑必入)
>可以参考官网设置-> 1 2 >> Fantasy_Lin_网友评论原文地址是:简书24K纯帅豆写的我也更新一下出处[删除]Fa 转自脚本之家 浅谈Android Studi ...
- Less开发指南(三)- 代码文件跟踪调试
案例背景:在大型网站中,css样式划分为多个模块文件,如reset.css,layout.css,skin.css等等(颗粒化越小,样式重用率越高),页面需要的时候引入它们即可! 回到less项目中这 ...