经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring » 查看文章
Springboot整合Rabbitmq之Confirm和Return机制
来源:jb51  时间:2022/2/28 15:24:46  对本文有异议

前言

之前专栏中,对Springboot整合Rabbitmq都有一系列的配置和说明,但总缺少一些必要的描述信息。导致很多看博客的小伙伴会私信问为什么需要这么配置的问题。

本篇博客重点进行Confirm 机制Return 机制的实现和说明。

为什么会有Confirm

RabbitMq中,针对数据由消息生产者消息队列推送时,通常情况如下所示(以 Routing 方式为例):

在这里插入图片描述

每个Virtual Host 虚拟机中,都会含有各自的ExchangeQueue,需要在rabbitmq web界面中针对可以访问该Virtual Host 虚拟机的用户进行设定。

有点类似数据库的概念,指定用户只能操作指定的数据库。

在使用交换机 Exchange时,消息生产者需要将消息通过Channel 管道将数据发送给MQ,但想过一个问题没有:

如何 确定 消息是否真的发送到了指定的 MQ 中呢?

在这里插入图片描述

MQ中,对此问题,提出有Confirm 机制,对其发送数据进行监听,让消息发送者知道消息的发送结果。

Springboot 整合 Mq 实现 Confirm 监听机制

依赖引入

开发测试主要的SpringBoot 版本为2.1.4.RELEASE

此时只需要引入指定的amqp依赖即可:

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

完整的pom依赖如下所示:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6.  
  7. <groupId>org.example</groupId>
  8. <artifactId>springboot-rabbitmq</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <parent>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-parent</artifactId>
  13. <version>2.1.4.RELEASE</version>
  14. <relativePath /> <!-- lookup parent from repository -->
  15. </parent>
  16. <properties>
  17. <java.version>1.8</java.version>
  18. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  19. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  20. </properties>
  21. <dependencies>
  22. <dependency>
  23. <groupId>org.springframework.boot</groupId>
  24. <artifactId>spring-boot-starter</artifactId>
  25. </dependency>
  26. <!-- 引入rabbitmq依赖 -->
  27. <artifactId>spring-boot-starter-amqp</artifactId>
  28. <artifactId>spring-boot-starter-web</artifactId>
  29. <artifactId>spring-boot-starter-test</artifactId>
  30. <scope>test</scope>
  31. <artifactId>spring-boot-configuration-processor</artifactId>
  32. <optional>true</optional>
  33. <groupId>org.projectlombok</groupId>
  34. <artifactId>lombok</artifactId>
  35. <version>1.16.20</version>
  36. <groupId>org.slf4j</groupId>
  37. <artifactId>slf4j-api</artifactId>
  38. <version>1.7.26</version>
  39. <artifactId>slf4j-log4j12</artifactId>
  40. </dependencies>
  41. </project>

增加配置文件,设定连接信息

增加配置文件,配置使用具体的Virtual HostUsernamePasswordHostPort等信息。

  1. server:
  2. port: 80
  3. spring:
  4. rabbitmq:
  5. host: xxxxxx
  6. port: 5672
  7. username: xiangjiao
  8. password: bunana
  9. virtual-host: /xiangjiao
  10. publisher-confirms: true #消息发送到转发器确认机制,是都确认回调
  11. publisher-returns: true

配置队列、交换机,以及对其进行绑定

指定交换机名称为:xiangjiao.exchange
队列名称为:xiangjiao.queue
使用Direct 直连模式,其中关联的Routingkey为:xiangjiao.routingKey

  1. package cn.linkpower.config;
  2. import org.springframework.amqp.core.*;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. @Configuration
  6. public class MQConfiguration {
  7. //队列名称
  8. public static final String QUEUQ_NAME = "xiangjiao.queue";
  9. //交换器名称
  10. public static final String EXCHANGE = "xiangjiao.exchange";
  11. //路由key
  12. public static final String ROUTING_KEY = "xiangjiao.routingKey";
  13. //创建队列
  14. @Bean
  15. public Queue getQueue(){
  16. // 另一种方式
  17. //QueueBuilder.durable(QUEUQ_NAME).build();
  18. return new Queue(QUEUQ_NAME);
  19. }
  20. //实例化交换机
  21. @Bean
  22. public DirectExchange getDirectExchange(){
  23. //DirectExchange(String name, boolean durable, boolean autoDelete)
  24. // 另一种方式:
  25. //ExchangeBuilder.directExchange(EXCHANGE).durable(true).build();
  26. /**
  27. * 参数一:交换机名称;<br>
  28. * 参数二:是否永久;<br>
  29. * 参数三:是否自动删除;<br>
  30. */
  31. return new DirectExchange(EXCHANGE, true, false);
  32. //绑定消息队列和交换机
  33. public Binding bindExchangeAndQueue(DirectExchange exchange,Queue queue){
  34. // 将 创建的 queue 和 exchange 进行绑定
  35. return BindingBuilder.bind(queue).to(exchange).with(ROUTING_KEY);
  36. }

编写mq消息发送服务

Springboot中,针对MQ消息的发送,采取RabbitTemplate模板进行数据的发送处理操作。

手动定义消息发送处理类,对其RabbitTemplate进行其他设置。

  1. package cn.linkpower.service;
  2.  
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.amqp.core.Message;
  7. import org.springframework.amqp.rabbit.connection.CorrelationData;
  8. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.stereotype.Component;
  11. @Slf4j
  12. @Component
  13. public class RabbitmqService implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
  14. @Autowired
  15. private RabbitTemplate rabbitTemplate;
  16. public void sendMessage(String exchange,String routingKey,Object msg) {
  17. // 设置交换机处理失败消息的模式 true 表示消息由交换机 到达不了队列时,会将消息重新返回给生产者
  18. // 如果不设置这个指令,则交换机向队列推送消息失败后,不会触发 setReturnCallback
  19. rabbitTemplate.setMandatory(true);
  20. //消息消费者确认收到消息后,手动ack回执
  21. rabbitTemplate.setConfirmCallback(this);
  22. // 暂时关闭 return 配置
  23. //rabbitTemplate.setReturnCallback(this);
  24. //发送消息
  25. rabbitTemplate.convertAndSend(exchange,routingKey,msg);
  26. }
  27. /**
  28. * 交换机并未将数据丢入指定的队列中时,触发
  29. * channel.basicPublish(exchange_name,next.getKey(), true, properties,next.getValue().getBytes());
  30. * 参数三:true 表示如果消息无法正常投递,则return给生产者 ;false 表示直接丢弃
  31. * @param message 消息对象
  32. * @param replyCode 错误码
  33. * @param replyText 错误信息
  34. * @param exchange 交换机
  35. * @param routingKey 路由键
  36. */
  37. @Override
  38. public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
  39. log.info("---- returnedMessage ----replyCode="+replyCode+" replyText="+replyText+" ");
  40. * 消息生产者发送消息至交换机时触发,用于判断交换机是否成功收到消息
  41. * @param correlationData 相关配置信息
  42. * @param ack exchange 交换机,判断交换机是否成功收到消息 true 表示交换机收到
  43. * @param cause 失败原因
  44. public void confirm(CorrelationData correlationData, boolean ack, String cause) {
  45. log.info("---- confirm ----ack="+ack+" cause="+String.valueOf(cause));
  46. log.info("correlationData -->"+correlationData.toString());
  47. if(ack){
  48. // 交换机接收到
  49. log.info("---- confirm ----ack==true cause="+cause);
  50. }else{
  51. // 没有接收到
  52. log.info("---- confirm ----ack==false cause="+cause);
  53. }
  54. }

编写消息发送接口

编写一个Controller,将产生的数据,通过自定义的RabbitmqService发送至指定的Exchange交换机中。

  1. package cn.linkpower.controller;
  2. import cn.linkpower.config.MQConfiguration;
  3. import cn.linkpower.service.RabbitmqService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Controller;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.ResponseBody;
  8. @Controller
  9. public class SendMessageTx {
  10. @Autowired
  11. private RabbitmqService rabbitmqService;
  12. @RequestMapping("/sendMoreMsgTx")
  13. @ResponseBody
  14. public String sendMoreMsgTx(){
  15. //发送10条消息
  16. for (int i = 0; i < 10; i++) {
  17. String msg = "msg"+i;
  18. System.out.println("发送消息 msg:"+msg);
  19. // xiangjiao.exchange 交换机
  20. // xiangjiao.routingKey 队列
  21. rabbitmqService.sendMessage(MQConfiguration.EXCHANGE, MQConfiguration.ROUTING_KEY, msg);
  22. //每两秒发送一次
  23. try {
  24. Thread.sleep(2000);
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }
  28. }
  29. return "send ok";
  30. }
  31. }

启动项目进行测试

正常测试

http://localhost/sendMoreMsgTx

从控制台中可以看到消息信息如下所示:

在这里插入图片描述

发现,消息信息发送,都是ACK 被确认的!

异常测试

异常测试,首先需要保证mq服务中没有对应的exchange交换机。还需要保证消息的发送者exchange信息修改。

将controller中对应的消息发送的方式修改如下:

  1. rabbitmqService.sendMessage("xiangjiao.exchangeError", MQConfiguration.ROUTING_KEY, msg);

在这里插入图片描述

重启项目,重新请求该接口,观察控制台数据信息展示:

在这里插入图片描述

截取其中的一条信息为例:

发送消息  msg:msg0
2022-02-28 10:34:58.686 ---- [rabbitConnectionFactory1] ---- INFO  cn.linkpower.service.RabbitmqService - ---- confirm ----ack=false  
cause=channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - 
no exchange 'xiangjiao.exchangeError' in vhost '/xiangjiao', class-id=60, method-id=40)

生产者Exchange中发送消息,如果消息并未成功发送,则会触发RabbitmqService中设定的confirm处理机制。

  1. rabbitTemplate.setConfirmCallback(this);
  2.  
  3. /**
  4. * 消息生产者发送消息至交换机时触发,用于判断交换机是否成功收到消息
  5. * @param correlationData 相关配置信息
  6. * @param ack exchange 交换机,判断交换机是否成功收到消息 true 表示交换机收到
  7. * @param cause 失败原因
  8. */
  9. @Override
  10. public void confirm(CorrelationData correlationData, boolean ack, String cause) {
  11. log.info("---- confirm ----ack="+ack+" cause="+String.valueOf(cause));
  12. log.info("correlationData -->"+correlationData.toString());
  13. if(ack){
  14. // 交换机接收到
  15. log.info("---- confirm ----ack==true cause="+cause);
  16. }else{
  17. // 没有接收到
  18. log.info("---- confirm ----ack==false cause="+cause);
  19. }
  20. }

什么是Return?

上面的配置中,采取Confirm机制,能够更好的保证消息生产者确认消息是否正常到达Exchange中

但是,在MQ中,由于使用ExchangeQueue进行了绑定,

如果某个队列宕机了,Exchange并未将消息发送匹配 Routing Key 的队列,那么消息就不能到达队列中!!!

在这里插入图片描述


mq中,对此情况设有另外一种监听机制:Return机制!

当消息由Exchange 未能传递到匹配的 queue 中,则会通过ReturnCallback根据用户的抉择,判断是否需要返回给消息生产者。

增加 ReturnCallback 监听并测试

修改 RabbitmqService 配置类

  1. package cn.linkpower.service;
  2.  
  3.  
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.amqp.core.Message;
  6. import org.springframework.amqp.rabbit.connection.CorrelationData;
  7. import org.springframework.amqp.rabbit.core.RabbitTemplate;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10.  
  11. @Slf4j
  12. @Component
  13. public class RabbitmqService implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
  14.  
  15. @Autowired
  16. private RabbitTemplate rabbitTemplate;
  17.  
  18. public void sendMessage(String exchange,String routingKey,Object msg) {
  19. // 设置交换机处理失败消息的模式 true 表示消息由交换机 到达不了队列时,会将消息重新返回给生产者
  20. // 如果不设置这个指令,则交换机向队列推送消息失败后,不会触发 setReturnCallback
  21. rabbitTemplate.setMandatory(true);
  22. //消息消费者确认收到消息后,手动ack回执
  23. rabbitTemplate.setConfirmCallback(this);
  24.  
  25. // return 配置
  26. rabbitTemplate.setReturnCallback(this);
  27. //发送消息
  28. rabbitTemplate.convertAndSend(exchange,routingKey,msg);
  29. }
  30.  
  31. /**
  32. * 交换机并未将数据丢入指定的队列中时,触发
  33. * channel.basicPublish(exchange_name,next.getKey(), true, properties,next.getValue().getBytes());
  34. * 参数三:true 表示如果消息无法正常投递,则return给生产者 ;false 表示直接丢弃
  35. * @param message 消息对象
  36. * @param replyCode 错误码
  37. * @param replyText 错误信息
  38. * @param exchange 交换机
  39. * @param routingKey 路由键
  40. */
  41. @Override
  42. public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
  43. log.info("---- returnedMessage ----replyCode="+replyCode+" replyText="+replyText+" ");
  44. }
  45.  
  46. /**
  47. * 消息生产者发送消息至交换机时触发,用于判断交换机是否成功收到消息
  48. * @param correlationData 相关配置信息
  49. * @param ack exchange 交换机,判断交换机是否成功收到消息 true 表示交换机收到
  50. * @param cause 失败原因
  51. */
  52. @Override
  53. public void confirm(CorrelationData correlationData, boolean ack, String cause) {
  54. log.info("---- confirm ----ack="+ack+" cause="+String.valueOf(cause));
  55. log.info("correlationData -->"+correlationData.toString());
  56. if(ack){
  57. // 交换机接收到
  58. log.info("---- confirm ----ack==true cause="+cause);
  59. }else{
  60. // 没有接收到
  61. log.info("---- confirm ----ack==false cause="+cause);
  62. }
  63. }
  64. }

【注意:】设置 setReturnCallback 后,如果需要保证消息未传递到指定的 queue,需要将消息返回生产者时,一定要增加下面配置:

  1. // 设置交换机处理失败消息的模式 true 表示消息由交换机 到达不了队列时,会将消息重新返回给生产者
  2. // 如果不设置这个指令,则交换机向队列推送消息失败后,不会触发 setReturnCallback
  3. rabbitTemplate.setMandatory(true);

测试

修改对应的测试类,保证交换机正确,但路由key不存在对应的队列即可。

  1. // xiangjiao.routingKey 存在对应的queue
  2. // xiangjiao.routingKey_error 不存在对应的 queue
  3. rabbitmqService.sendMessage(MQConfiguration.EXCHANGE, "xiangjiao.routingKey_error", msg);

重启项目,访问接口,进行测试:

在这里插入图片描述

消息发送给Exchange成功,但是通过ExchangeQueue中推送数据时 失败,经过ReturnCallback 的 returnedMessage捕获监听!

总结

通过配置ConfirmCallbackReturnCallback,便能实现消息生产者到交换机消息由exchange到queue这个链路的安全性!

在这里插入图片描述

都是出现问题,或者正常后,给生产者方进行反馈。

相关代码下载

gitee 代码下载地址

到此这篇关于Springboot整合Rabbitmq之Confirm和Return详解的文章就介绍到这了,更多相关Springboot整合Rabbitmq内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号