Spring Boot 入门之消息中间件篇(五)

原文地址:Spring Boot 入门之消息中间件篇(五)
博客地址:http://www.extlight.com

一、前言

在消息中间件中有 2 个重要的概念:消息代理和目的地。当消息发送者发送消息后,消息就被消息代理接管,消息代理保证消息传递到指定目的地。

我们常用的消息代理有 JMS 和 AMQP 规范。对应地,它们常见的实现分别是 ActiveMQ 和 RabbitMQ。

上篇文章《Spring Boot 入门之缓存和 NoSQL 篇(四)》

二、整合 ActiveMQ

2.1 添加依赖

 
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-activemq</artifactId>
  4. </dependency>
  5.  
  6. <!-- 如果需要配置连接池,添加如下依赖 -->
  7. <dependency>
  8. <groupId>org.apache.activemq</groupId>
  9. <artifactId>activemq-pool</artifactId>
  10. </dependency>

2.2 添加配置

 
  1. # activemq 配置
  2. spring.activemq.broker-url=tcp://192.168.2.12:61616
  3. spring.activemq.user=admin
  4. spring.activemq.password=admin
  5. spring.activemq.pool.enabled=false
  6. spring.activemq.pool.max-connections=50
  7. # 使用发布/订阅模式时,下边配置需要设置成 true
  8. spring.jms.pub-sub-domain=false

此处 spring.activemq.pool.enabled=false,表示关闭连接池。

2.3 编码

配置类:

 
  1. @Configuration
  2. public class JmsConfirguration {
  3.  
  4. public static final String QUEUE_NAME = "activemq_queue";
  5.  
  6. public static final String TOPIC_NAME = "activemq_topic";
  7.  
  8. @Bean
  9. public Queue queue() {
  10. return new ActiveMQQueue(QUEUE_NAME);
  11. }
  12.  
  13. @Bean
  14. public Topic topic() {
  15. return new ActiveMQTopic(TOPIC_NAME);
  16. }
  17. }

负责创建队列和主题。

消息生产者:

 
  1. @Component
  2. public class JmsSender {
  3.  
  4. @Autowired
  5. private Queue queue;
  6.  
  7. @Autowired
  8. private Topic topic;
  9.  
  10. @Autowired
  11. private JmsMessagingTemplate jmsTemplate;
  12.  
  13. public void sendByQueue(String message) {
  14. this.jmsTemplate.convertAndSend(queue, message);
  15. }
  16.  
  17. public void sendByTopic(String message) {
  18. this.jmsTemplate.convertAndSend(topic, message);
  19. }
  20. }

消息消费者:

 
  1. @Component
  2. public class JmsReceiver {
  3.  
  4. @JmsListener(destination = JmsConfirguration.QUEUE_NAME)
  5. public void receiveByQueue(String message) {
  6. System.out.println("接收队列消息:" + message);
  7. }
  8.  
  9. @JmsListener(destination = JmsConfirguration.TOPIC_NAME)
  10. public void receiveByTopic(String message) {
  11. System.out.println("接收主题消息:" + message);
  12. }
  13. }

消息消费者使用 @JmsListener 注解监听消息。

2.4 测试

 
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class JmsTest {
  4.  
  5. @Autowired
  6. private JmsSender sender;
  7.  
  8. @Test
  9. public void testSendByQueue() {
  10. for (int i = 1; i < 6; i++) {
  11. this.sender.sendByQueue("hello activemq queue " + i);
  12. }
  13. }
  14.  
  15. @Test
  16. public void testSendByTopic() {
  17. for (int i = 1; i < 6; i++) {
  18. this.sender.sendByTopic("hello activemq topic " + i);
  19. }
  20. }
  21. }

打印结果:

 
  1. 接收队列消息:hello activemq queue 1
  2. 接收队列消息:hello activemq queue 2
  3. 接收队列消息:hello activemq queue 3
  4. 接收队列消息:hello activemq queue 4
  5. 接收队列消息:hello activemq queue 5

测试发布/订阅模式时,设置 spring.jms.pub-sub-domain=true

 
  1. 接收主题消息:hello activemq topic 1
  2. 接收主题消息:hello activemq topic 2
  3. 接收主题消息:hello activemq topic 3
  4. 接收主题消息:hello activemq topic 4
  5. 接收主题消息:hello activemq topic 5

三、整合 RabbitMQ

3.1 添加依赖

 
  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-amqp</artifactId>
  4. </dependency>

3.2 添加配置

 
  1. spring.rabbitmq.host=192.168.2.30
  2. spring.rabbitmq.port=5672
  3. spring.rabbitmq.username=light
  4. spring.rabbitmq.password=light
  5. spring.rabbitmq.virtual-host=/test

3.3 编码

配置类:

 
  1. @Configuration
  2. public class AmqpConfirguration {
  3.  
  4. //=============简单、工作队列模式===============
  5.  
  6. public static final String SIMPLE_QUEUE = "simple_queue";
  7.  
  8. @Bean
  9. public Queue queue() {
  10. return new Queue(SIMPLE_QUEUE, true);
  11. }
  12.  
  13. //===============发布/订阅模式============
  14.  
  15. public static final String PS_QUEUE_1 = "ps_queue_1";
  16. public static final String PS_QUEUE_2 = "ps_queue_2";
  17. public static final String FANOUT_EXCHANGE = "fanout_exchange";
  18.  
  19. @Bean
  20. public Queue psQueue1() {
  21. return new Queue(PS_QUEUE_1, true);
  22. }
  23.  
  24. @Bean
  25. public Queue psQueue2() {
  26. return new Queue(PS_QUEUE_2, true);
  27. }
  28.  
  29. @Bean
  30. public FanoutExchange fanoutExchange() {
  31. return new FanoutExchange(FANOUT_EXCHANGE);
  32. }
  33.  
  34. @Bean
  35. public Binding fanoutBinding1() {
  36. return BindingBuilder.bind(psQueue1()).to(fanoutExchange());
  37. }
  38.  
  39. @Bean
  40. public Binding fanoutBinding2() {
  41. return BindingBuilder.bind(psQueue2()).to(fanoutExchange());
  42. }
  43.  
  44. //===============路由模式============
  45.  
  46. public static final String ROUTING_QUEUE_1 = "routing_queue_1";
  47. public static final String ROUTING_QUEUE_2 = "routing_queue_2";
  48. public static final String DIRECT_EXCHANGE = "direct_exchange";
  49.  
  50. @Bean
  51. public Queue routingQueue1() {
  52. return new Queue(ROUTING_QUEUE_1, true);
  53. }
  54.  
  55. @Bean
  56. public Queue routingQueue2() {
  57. return new Queue(ROUTING_QUEUE_2, true);
  58. }
  59.  
  60. @Bean
  61. public DirectExchange directExchange() {
  62. return new DirectExchange(DIRECT_EXCHANGE);
  63. }
  64.  
  65. @Bean
  66. public Binding directBinding1() {
  67. return BindingBuilder.bind(routingQueue1()).to(directExchange()).with("user");
  68. }
  69.  
  70. @Bean
  71. public Binding directBinding2() {
  72. return BindingBuilder.bind(routingQueue2()).to(directExchange()).with("order");
  73. }
  74.  
  75. //===============主题模式============
  76.  
  77. public static final String TOPIC_QUEUE_1 = "topic_queue_1";
  78. public static final String TOPIC_QUEUE_2 = "topic_queue_2";
  79. public static final String TOPIC_EXCHANGE = "topic_exchange";
  80.  
  81. @Bean
  82. public Queue topicQueue1() {
  83. return new Queue(TOPIC_QUEUE_1, true);
  84. }
  85.  
  86. @Bean
  87. public Queue topicQueue2() {
  88. return new Queue(TOPIC_QUEUE_2, true);
  89. }
  90.  
  91. @Bean
  92. public TopicExchange topicExchange() {
  93. return new TopicExchange(TOPIC_EXCHANGE);
  94. }
  95.  
  96. @Bean
  97. public Binding topicBinding1() {
  98. return BindingBuilder.bind(topicQueue1()).to(topicExchange()).with("user.add");
  99. }
  100.  
  101. @Bean
  102. public Binding topicBinding2() {
  103. return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("user.#");
  104. }
  105.  
  106. }

RabbitMQ 有多种工作模式,因此配置比较多。想了解相关内容的读者可以查看本站的《RabbitMQ 工作模式介绍》或者自行百度相关资料。

消息生产者:

 
  1. @Component
  2. public class AmqpSender {
  3.  
  4. @Autowired
  5. private AmqpTemplate amqpTemplate;
  6.  
  7. /**
  8. * 简单模式发送
  9. *
  10. * @param message
  11. */
  12. public void simpleSend(String message) {
  13. this.amqpTemplate.convertAndSend(AmqpConfirguration.SIMPLE_QUEUE, message);
  14. }
  15.  
  16. /**
  17. * 发布/订阅模式发送
  18. *
  19. * @param message
  20. */
  21. public void psSend(String message) {
  22. this.amqpTemplate.convertAndSend(AmqpConfirguration.FANOUT_EXCHANGE, "", message);
  23. }
  24.  
  25. /**
  26. * 路由模式发送
  27. *
  28. * @param message
  29. */
  30. public void routingSend(String routingKey, String message) {
  31. this.amqpTemplate.convertAndSend(AmqpConfirguration.DIRECT_EXCHANGE, routingKey, message);
  32. }
  33.  
  34. /**
  35. * 主题模式发送
  36. *
  37. * @param routingKey
  38. * @param message
  39. */
  40. public void topicSend(String routingKey, String message) {
  41. this.amqpTemplate.convertAndSend(AmqpConfirguration.TOPIC_EXCHANGE, routingKey, message);
  42. }
  43. }

消息消费者:

 
  1. @Component
  2. public class AmqpReceiver {
  3.  
  4. /**
  5. * 简单模式接收
  6. *
  7. * @param message
  8. */
  9. @RabbitListener(queues = AmqpConfirguration.SIMPLE_QUEUE)
  10. public void simpleReceive(String message) {
  11. System.out.println("接收消息:" + message);
  12. }
  13.  
  14. /**
  15. * 发布/订阅模式接收
  16. *
  17. * @param message
  18. */
  19. @RabbitListener(queues = AmqpConfirguration.PS_QUEUE_1)
  20. public void psReceive1(String message) {
  21. System.out.println(AmqpConfirguration.PS_QUEUE_1 + "接收消息:" + message);
  22. }
  23.  
  24. @RabbitListener(queues = AmqpConfirguration.PS_QUEUE_2)
  25. public void psReceive2(String message) {
  26. System.out.println(AmqpConfirguration.PS_QUEUE_2 + "接收消息:" + message);
  27. }
  28.  
  29. /**
  30. * 路由模式接收
  31. *
  32. * @param message
  33. */
  34. @RabbitListener(queues = AmqpConfirguration.ROUTING_QUEUE_1)
  35. public void routingReceive1(String message) {
  36. System.out.println(AmqpConfirguration.ROUTING_QUEUE_1 + "接收消息:" + message);
  37. }
  38.  
  39. @RabbitListener(queues = AmqpConfirguration.ROUTING_QUEUE_2)
  40. public void routingReceive2(String message) {
  41. System.out.println(AmqpConfirguration.ROUTING_QUEUE_2 + "接收消息:" + message);
  42. }
  43.  
  44. /**
  45. * 主题模式接收
  46. *
  47. * @param message
  48. */
  49. @RabbitListener(queues = AmqpConfirguration.TOPIC_QUEUE_1)
  50. public void topicReceive1(String message) {
  51. System.out.println(AmqpConfirguration.TOPIC_QUEUE_1 + "接收消息:" + message);
  52. }
  53.  
  54. @RabbitListener(queues = AmqpConfirguration.TOPIC_QUEUE_2)
  55. public void topicReceive2(String message) {
  56. System.out.println(AmqpConfirguration.TOPIC_QUEUE_2 + "接收消息:" + message);
  57. }
  58. }

消息消费者使用 @RabbitListener 注解监听消息。

3.4 测试

 
  1. @RunWith(SpringRunner.class)
  2. @SpringBootTest
  3. public class AmqpTest {
  4.  
  5. @Autowired
  6. private AmqpSender sender;
  7.  
  8. @Test
  9. public void testSimpleSend() {
  10. for (int i = 1; i < 6; i++) {
  11. this.sender.simpleSend("test simpleSend " + i);
  12. }
  13. }
  14.  
  15. @Test
  16. public void testPsSend() {
  17. for (int i = 1; i < 6; i++) {
  18. this.sender.psSend("test psSend " + i);
  19. }
  20. }
  21.  
  22. @Test
  23. public void testRoutingSend() {
  24. for (int i = 1; i < 6; i++) {
  25. this.sender.routingSend("order", "test routingSend " + i);
  26. }
  27. }
  28.  
  29. @Test
  30. public void testTopicSend() {
  31. for (int i = 1; i < 6; i++) {
  32. this.sender.topicSend("user.add", "test topicSend " + i);
  33. }
  34. }
  35. }

测试结果略过。。。

踩坑提醒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 选项中手动添加对应的队列。

四、源码下载

五、参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值