原文地址:Spring Boot 入门之消息中间件篇(五)

博客地址:http://www.extlight.com

一、前言

在消息中间件中有 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

解决方案:

  1. 请确保用户名和密码是否正确,需要注意的是用户名和密码的值是否包含空格或制表符(笔者测试时就是因为密码多了一个制表符导致认证失败)。

  2. 如果测试账户使用的是 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 入门之消息中间件篇(五)的更多相关文章

  1. Spring Boot 入门之消息中间件篇(转发)

    一.前言 在消息中间件中有 2 个重要的概念:消息代理和目的地.当消息发送者发送消息后,消息就被消息代理接管,消息代理保证消息传递到指定目的地. 我们常用的消息代理有 JMS 和 AMQP 规范.对应 ...

  2. Spring Boot 入门之基础篇(一)

    原文地址:Spring Boot 入门之基础篇(一) 博客地址:http://www.extlight.com 一.前言 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是 ...

  3. Spring Boot 入门之 Web 篇(二)

    原文地址:Spring Boot 入门之 Web 篇(二) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之基础篇(一)>介绍了 ...

  4. Spring Boot 入门之单元测试篇(五)

    博客地址:http://www.moonxy.com 一.前言 JUnit 是一个由 Java 语言编写的开源的回归测试(回归测试是指重复以前全部或部分的相同测试)框架,由Erich Gamma 和 ...

  5. Spring Boot 入门之 Cache 篇(四)

    博客地址:http://www.moonxy.com 一.前言 Spring Cache 对 Cahce 进行了抽象,提供了 @Cacheable.@CachePut.@CacheEvict 等注解. ...

  6. Spring Boot入门系列(十五)Spring Boot 开发环境热部署

    在实际的项目开发过中,当我们修改了某个java类文件时,需要手动重新编译.然后重新启动程序的,整个过程比较麻烦,特别是项目启动慢的时候,更是影响开发效率.其实Spring Boot的项目碰到这种情况, ...

  7. Spring Boot 入门之持久层篇(三)

    原文地址:Spring Boot 入门之持久层篇(三) 博客地址:http://www.extlight.com 一.前言 上一篇<Spring Boot 入门之 Web 篇(二)>介绍了 ...

  8. spring boot入门教程——Spring Boot快速入门指南

    Spring Boot已成为当今最流行的微服务开发框架,本文是如何使用Spring Boot快速开始Web微服务开发的指南,我们将使创建一个可运行的包含内嵌Web容器(默认使用的是Tomcat)的可运 ...

  9. Spring Cloud 入门 之 Zuul 篇(五)

    原文地址:Spring Cloud 入门 之 Zuul 篇(五) 博客地址:http://www.extlight.com 一.前言 随着业务的扩展,微服务会不对增加,相应的其对外开放的 API 接口 ...

随机推荐

  1. Python基础笔记之同时装了Python3和Python2,怎么在命令行使用pip

    我们在安装Python3(>=3.3)时,Python的安装包实际上在系统中安装了一个启动器py.exe,默认放置在文件夹C:\Windows\下面.这个启动器允许我们指定使用Python2还是 ...

  2. Python3.6(windows系统)解决编码问题

    Python3.6(windows系统)解决编码问题 1,py文件代码: import urllib.request url = "http://www.douban.com/" ...

  3. linux内核分析第五周-分析system_call中断处理过程

    本实验目的:通过以一个简单的menu小程序,跟踪系统调用的过程,分析与总结系统调用的机制和三层进入的过程. 实验原理:系统调用处理过程与中断处理的机制 系统调用是通过软中断指令 INT 0x80 实现 ...

  4. Linux基础入门(实验楼实验)

    实验一 Linux系统简介 Linux和windows.Mac OS一样是一种操作系统.最早流行起来的操作系统是UNIX,但由于其过度商业化,价格昂贵,因此在校园里人们大多选择MINIX.1991年, ...

  5. SQL语句主要的分类

    SQL语言的命令通常分为四类1.数据定义语言(DDL) 创建.修改或删除数据库中各种对象,包括表.视图.索引等. 命令:CREATE TABLE , CREATE VIEW, CREATE INDEX ...

  6. Codeforces Round #319 (Div. 2) C. Vasya and Petya's Game 数学

    C. Vasya and Petya's Game time limit per test 1 second memory limit per test 256 megabytes input sta ...

  7. php 格式化时间

    <?php echo date("Y/m/d") . "<br>"; echo date("Y.m.d") . " ...

  8. 【Docker】数据库动态授权组件在Kubernetes集群下的测试过程记录

    背景 我们都知道出于安全性考虑,生产环境的权限一般都是要做最小化控制,尤其是数据库的操作授权,更是重中之重. 博主所在公司使用的是Kubernetes(k8s)进行的集群容器管理,因为容器发布时的IP ...

  9. 不能访问虚拟机上的nginx网站

    VMware虚拟机上配置nginx后,本机无法访问问题 转自:http://www.server110.com/nginx/201407/10794.html 把nginx装在CentOS上,用本机访 ...

  10. 雷林鹏分享:Ruby Dir 类和方法

    Ruby Dir 类和方法 Dir 是一个表示用于给出操作系统中目录中的文件名的目录流.Dir 类也拥有与目录相关的操作,比如通配符文件名匹配.改变工作目录等. 类方法 序号方法 & 描述 1 ...