原文地址: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笔记 #11# 统计图定制化

    将数据可视化有许多选择: 图的类型 定制化方式 选择什么样的表现方式通常取决于: 数据 你想表达什么 1.Labels # Basic scatter plot, log scale plt.scat ...

  2. JS地址自动返填技术

    系统设计地址为省市县三级联动,规范是规范了,但是无形中增加了系统操作的时间成本,因此设计地址自动返填技术,只要把地址拷贝到详细地址框中,可以自动返填到省市县三级联动的下拉框中. 还好洒家的大学不是混过 ...

  3. Firefox管理已经保存的账号和密码

    https://support.mozilla.org/en-US/kb/password-manager-remember-delete-change-and-import You can easi ...

  4. R中的路径设置

    软件的路径设置对于电脑的内存管理和自己的寻根究底十分重要.所以,合理的设置R中相关路径,能更加方便快捷的管理自己的相关文件,提高学习R语言的效率,建立自己的习惯体系. R中的路径设置主要有以下几个方面 ...

  5. python2.7和python3.6共存,使用pip安装第三方库

    因为一般情况下,window命令行运行pip,默认的情况是运行python3.6的pip,安装第三方库的路径也是python3.6,安装路径是: 如何运行在2.7环境下安装PIP呢?有网上的教程说需要 ...

  6. python输出日期时间

    import datetime base = datetime.datetime.today() , ): print(base + datetime.timedelta(days=x))

  7. python 获取二维数组所有元素

    import itertools original_list = [[,,],[,,], [], [,,]] new_merged_list = list(itertools.chain(*origi ...

  8. 如何优雅地发布Hexo博客

    前言 就目前而言,我所知道的发布Hexo的博客有如下几种: 1.原始方式,也就是在服务器上编写md文件,然后利用hexo g来生成,详见:hexo从零开始到搭建完整: 2.利用github+hook来 ...

  9. spark 累加历史 + 统计全部 + 行转列

    spark 累加历史主要用到了窗口函数,而进行全部统计,则需要用到rollup函数 1  应用场景: 1.我们需要统计用户的总使用时长(累加历史) 2.前台展现页面需要对多个维度进行查询,如:产品.地 ...

  10. hdu3549网络流之最大流

    Ford-Fulkerson方法:dfs实现 dfs  140ms #include<map> #include<set> #include<cmath> #inc ...