MQ系列 | RabbitMQ 消息确认机制
RabbitMQ 消息确认机制
温馨提示:基于
JDK17、SpringBoot 2.1.8.RELEASE版本,由于RabbitMQ在SpringBoot3+的配置项有所不同, 所以请严格按照该本版来使用,挖一坑:【后续会出一个SpringBoot3+版本的配置相关教程】
架构
概念
保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍 为此引入确认机制
- 生产者确认回调:
publisherconfirmCallback - 生产者退回回调:
publisherreturnCallback未投递到queue退回模式 - 消费者确认:
consumerack确认机制 
ComfirmCallback【生产者确认回调】
- 概念:
ComfirmCallback是生产者消息确认机制的一部分。当生产者发送消息到RabbitMQ的交换器(Exchange)后,RabbitMQ会返回一个确认消息给生产者,这个确认过程可以通过ConfirmCallback来处理。 - 原理:生产者发送消息时,会为每条消息关联一个 
CorrelationData对象,这个对象可以包含一些自定义的信息,用于跟踪消息。当消息成功发送到交换器后,RabbitMQ会触发ConfirmCallback接口中的【confirm】 方法。 
ReturnCallback【生产者退回回调】
- 概念:
ReturnCallback用于处理消息无法被正确路由到队列的情况。当生产者发送消息到交换器后,如果交换器无法将消息路由到任何队列(例如,没有匹配的绑定规则或者队列不存在),消息会被退回给生产者,这个退回过程可以通过ReturnCallback来处理。 - 原理:生产者需要配置消息退回机制,并且实现 
ReturnCallback接口。当消息被退回时,ReturnCallback接口中的 【returnedMessage】 方法会被触发。 
BasicAck【消费者确认】
- 概念: 
BasicAck是消费者确认消息的一种方式。在RabbitMQ中,消费者接收到消息后,需要向RabbitMQ服务器确认消息已经被正确处理,这样RabbitMQ才会从队列中删除该消息。BasicAck是手动确认模式下用于确认消息的方法之一。 - 原理:消费者在手动确认模式下,从队列中接收消息并进行处理。当处理完成且没有出现问题时,消费者可以使用 Channel 对象的
basicAck方法来确认消息。basicAck方法需要传入两个参数:deliveryTag和multiple。deliveryTag是消息的唯一标识,由 RabbitMQ 服务器分配;multiple是一个布尔值,用于表示是否确认多条消息。 
生产者确认回调 ConfirmCallback
添加配置
# 开启生产者消息确认机制
spring.rabbitmq.publisher-confirms=true
添加 RabbitMQConfig
自定义 confirmCallback#confirm
CorrelationData:当前消息唯一关联数据【消息的唯一Id】ack:是否成功收到状态cause:失败原因
@Configuration
@Slf4j
public class RabbitMQConfig {
     @Autowired
     RabbitTemplate rabbitTemplate;
     @PostConstruct //创建RabbitMQConfig对象后,执行这个方法
     public void initRabbitTemplate() {
        //设置确认回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
        /**
         * @param correlationData 当前消息的唯一关联数据(这个消息的唯一id)
         * @param ack 消息是否成功收到
         * @param cause  失败的原因
        */
        @Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        	log.info("confirm=>correlationData【{}】=>ack【{}】=>cause【{}】 ", correlationData, ack, cause);
        }
    });
}
测试:生产者确认
@Slf4j
@RestController
public class ProducerController {
    @Autowired
    RabbitTemplate rabbitTemplate;
    /**
     * 发送消息
     *
     * @param num
     */
    @GetMapping("/send")
    public void sendMessage(@RequestParam("num") int num) {
        for (int i = 0; i < num; i++) {
            if (i % 2 == 0) {
                OrderReturnReasonEntity data = new OrderReturnReasonEntity();
                data.setId(1L).setCreateTime(new Date()).setName("测试-" + i);
                rabbitTemplate.convertAndSend("hello-java-exchange", "hello-java", data);
            } else {
                OrderEntity data = new OrderEntity();
                data.setOrderSn(UUID.randomUUID().toString());
                rabbitTemplate.convertAndSend("hello-java-exchange", "hello-java", data);
            }
        }
        log.info("发送消息: {}条完成", num);
    }
}
消息发送成功,生产者确认回调生效,注意下这里的correlationData的数据为null

修改下发送信息
ProducerController#sendMessage中添加当前消息的唯一id
rabbitTemplate.convertAndSend("hello-java-exchange", "hello-java", data,new CorralationData(UUID.randomUUID().toString()));
- 这里
correlationData.getId()(也就是UUID)可以帮助开发者在多个消息发送场景中,唯一地标识每条消息,从而准确地跟踪某一条特定消息的发送状态,是发送成功还是失败。 
测试2:消息唯一Id

生产者回退回调 ReturnCallback
confirm 模式只能保证消息到达 broker,不能保证消息准确投递到目标 queue 里,我们需要保证消息一定要投递到目标 queue 里,此时就需要用到
return 退回模式。
添加配置
spring.rabbitmq.publisher-returns=true # 开启生产者消息抵达队列的确认
spring.rabbitmq.template.mandatory=true # 只要抵达队列,以异步发送优先回调 return confirm,【发送端确认,默认false】,当交换机无法找到队列时,false【直接丢弃数据】,true【会将消息返回给生产者】
RabbitMQConfig 配置类添加
 只有当前消息不能抵达队列才会触发这个回调
//设置消息抵达队列的确认回调
    rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
        /**
          * 只要消息没有投递给指定的队列,就触发这个失败回调
          * @param message  投递失败的消息详细信息
          * @param replyCode  回复的状态码
          * @param replyText  回复的文本内容
          * @param exchange  当时这个消息发给哪个交换机
          * @param routingKey  当时这个消息用哪个路由键
        */
        @Override
        public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
            log.error("消息发送失败,消息:{},失败码:{},失败原因:{},发送的交换机:{},路由键:{}", message, replyCode, replyText, exchange, replyCode);
	}
});
修改发送消息的路由键
	ProducerController#sendMessage发送消息核心代码修改,将其中一个路由键修改成  hello2-java(或者修改成没有可绑定的队列即可)
rabbitTemplate.convertAndSend("hello-java-exchange", "hello2-java", data);// 修改这个路由键为 hello2-java
测试:生产者退回回调
执行发送消息,结果如下

- 消息成功到达 
Broker服务器,消息确认机制生效,打印confirm相关信息 - 消息接收失败,生产者回退模式生效,其中 失败原因:【
NO_ROUTE】没有路由到队列,其中路由键:【hello2-java】,交换机和失败码等信息都打印出来 
消费者确认:Ack
	消费者收到消息,成功处理发送 Ack 给 Broker
	消费者收到消息自动确认,但是无法确认消息是否被处理完成或者成功处理,需要手动开启ack
测试:默认自动 ack
ProducerController 添加一个发送消息方法
@GetMapping("sendMQ/{num}")
public void sendMQ(@PathVariable int num) {
    for (int i = 0; i < num; i++) {
        OrderReturnReasonEntity data = new OrderReturnReasonEntity();
        data.setId(1L).setCreateTime(new Date()).setName("测试-");
        rabbitTemplate.convertAndSend("hello-java-exchange", "hello-java",
                                      data,new CorrelationData(UUID.randomUUID().toString()));
    }
    log.info("发送消息: {}条完成", num);
}
发送10条消息

客户端接收到消息,开始处理,处理一条消息完成后,接收下一条消息宕机

收到消息处理一条完成,队列剩下9条消息


此时直接结束服务,代表宕机,队列中的未确认的消息自动被确认

手动ack :添加配置
spring.rabbitmq.listener.simple.acknowleage-mode=maunal # 手动ack消息
发送10条消息,收到后模拟宕机,发现消息不会自动确认


宕机后,消息回到准备状态,没有确认

修改接收消息代码
添加消费者消息确认ack
@RabbitHandler
public void receiveOrderReturnReason(Message message, OrderReturnReasonEntity content, Channel channel) {
    log.info("接收到消息:{}", content);
    //消息体
    byte[] body = message.getBody();
    //消息头配置
    MessageProperties messageProperties = message.getMessageProperties();
    log.info("消息处理完成:消息体内容:{}", content.getName());
    //channel内按顺序自增
    long deliveryTag = messageProperties.getDeliveryTag();
    log.info("deliveryTag:{}", deliveryTag);
    //签收获取,非批量模式
    try {
       if (deliveryTag % 2 == 0) {
           channel.basicAck(deliveryTag, false);
           log.info("签收货物:{}", deliveryTag);
       } else {
           // 拒签 requeue=false丢弃 requeue=true 发回服务器,服务器重新入队
           // long deliveryTag, boolean multiple, boolean requeue
           channel.basicNack(deliveryTag, false, true);
           // long deliveryTag, boolean requeue
           // channel.basicReject(deliveryTag, false);
           log.info("拒绝签收货物:{}", deliveryTag);
       }
    } catch (IOException e) {
        //网络中断
    }
}
- 消息确认ack,从消息头中获取 
deliveryTag deliveryTag:是消息传递标签,它是一个正整数,用于唯一标识一条消息的投递。这个标签主要用于消息确认机制。- 消息投递顺序:在通道内【
channel】内,消息按照顺序被投递,并且【deliveryTag】值是单调递增的 - 重试机制:可以根据未确认
deliveryTag重新将消息发送给其他消费者或者在一定时间后重新发送给同一消费者。 
- 消息投递顺序:在通道内【
 channel.basicAck(deliveryTag,false)手动确认,false非批量channel.basicNack(deliveryTag,false,false)拒绝确认deliveryTag标识消息的标签,multiple=false非批量,requeue=false丢弃(requeue=true发回服务器,服务器重新入队)
channel.basicReject(deliverTag,false)拒绝确认,不能批量
测试:重新入队requeue=true
	发送10条消息,channel.basicNack(deliveryTag,false,true) 中 requeue=true ,消息重新入队,再次消费

所有消息消费完毕

测试:丢弃消息 requeue=false
发送10条消息,channel.basicNack(deliveryTag,false,false) 中 requeue=false ,消息直接丢弃
拒绝的消息直接丢弃

MQ系列 | RabbitMQ 消息确认机制的更多相关文章
- RabbitMQ消息确认机制
		
文章目录 1. 事务机制2. Confirm模式2.1 生产者2.1.1 普通Confirm模式2.1.2 批量Confirm模式2.1.3 异步Confirm模式2.2 消费者3. 其他 消费者如何 ...
 - RabbitMQ 消息确认机制
		
消息确认机制 在之前异常处理部分就已经写了,对于consumer的异常退出导致消息丢失,可以时候consumer的消息确认机制.重复的就不说了,这里说一些不一样的. consumer的消息确认机制 当 ...
 - RabbitMQ 消息确认机制以及lazy queue+ disk消息持久化
		
一:Basic的一些属性,一些方法 1. 消费端的确认 自动确认: message出队列的时候就自动确认[broke] basicget... 手工确认: message出队列之后,要应用程序自己去确 ...
 - springboot + rabbitmq 用了消息确认机制,感觉掉坑里了
		
本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI ...
 - SpringBoot(九)RabbitMQ安装及配置和使用,消息确认机制
		
Windows下RabbitMQ安装及配置地址: https://blog.csdn.net/zhm3023/article/details/82217222RabbitMQ(四)订阅模式:https ...
 - RabbitMQ学习第七章:消息确认机制之事务机制
		
RabbitMQ消息确认机制之事务机制. RabbitMQ中,我们可以通过持久化数据 解决RabbitMQ服务器异常 的数据丢失问题. 问题:生产者将消息发送出去,消息到底有没有到达RabbitMQ服 ...
 - RabbitMQ消息确认(发送确认,接收确认)
		
前面几篇记录了收发消息的demo,今天记录下关于 消息确认方面的 问题. 下面是几个问题: 1.为什么要进行消息确认? 2.rabbitmq消息确认 机制是什么样的? 3.发送方如何确认消息发送成功? ...
 - RabbitMQ学习笔记之五种模式及消息确认机制
		
本文详细介绍简单模式Simple.工作模式Work.发布订阅模式Publish/Subscribe.Topic.Routing. Maven依赖引用 <dependencies> < ...
 - 消息队列RabbitMQ(三):消息确认机制
		
引言 RabbitMQ的模型是生产者发送信息到 Broker (代理),消费者从 Broker 中取出信息.但是生产者怎么知道消息是否真的发送到 Broker 中了呢?Broker 又怎么知道消息到底 ...
 - (转)RabbitMQ消息队列(九):Publisher的消息确认机制
		
在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...
 
随机推荐
- Wpf使用NLog将日志输出到LogViewer
			
1 LogViewer LogViewer是通过UDP传输的高性能实时log查看器. 具有一下特性: 通过UDP读取日志 通过文件导入日志 导出日志到一个文件中 排序.过滤(日志树,日志等级)和查找 ...
 - linux内核调试痛点之函数参数抓捕记
			
1.linux内核调试工具crash并不能直接显示函数参数,而这个对调试又非常重要 下面是工作中一个实际的问题,我们的进程hang在如下一个内核栈中了,通过栈回溯可知是打开了一个nfs3的网盘文件或者 ...
 - Java日期时间API系列22-----Jdk8中java.time包中的新的日期时间API类,Month月份和DayOfWeek星期的计算。
			
Java8中为月份和星期新增的了,Month和DayOfWeek,来处理月份和星期的特殊问题,这2个类都是枚举类,对Month.DayOfWeek源码说明和简单应用,月份英文,月份英文简称,月份中文, ...
 - 妙用编辑器:使用Notepad--列编辑功能批量生成维护命令
			
应用场景 在日常工作中,维护人员可能会要批量生成一些配置命令,示例如下:添加12个分组 ADD GROUP:GID=1,FCN=646322; ADD GROUP:GID=1,FCN=646322; ...
 - 使用 OpenFunction 在任何基础设施上运行 Serverless 工作负载
			
作者: 霍秉杰:KubeSphere 可观测性.边缘计算和 Serverless 团队负责人,Fluent Operator 和 OpenFunction 项目的创始人,还是多个可观测性开源项目包括 ...
 - appium环境搭建及命令行启动sdk模拟器-附踩坑以及解决过程
			
安装教程这里就不阐述了,网上一大堆教程,下载完成后安装然后配置对应的环境变量即可 android sdk及java home配置: path配置: %ANDROID_HOME%\platform-to ...
 - git知识点,常用命令
			
git理论知识 git的服务器端(remote)端包含多个repository,每个repository可以理解为一个项目. 而每个repository下有多个branch."origin& ...
 - JS 转盘抽奖效果
			
阅读原文,微信扫描二维码, 手机关注公共号酒酒酒酒,搜索 JS 转盘抽奖效果 效果图 前置条件: 开发环境:windows 开发框架:js 编辑器:HbuilderX 正文开始 <!DOCTYP ...
 - 问题:深度学习时代的初期最为火热的AI安全问题已经很少有人讨论了,那么是不是已经解决该问题了呢?
			
答案: 先说结果,该问题并没有被解决. 之所以该问题已经不是最初的那么火热的讨论和研究热点了,其主要原因是大家发现这个神经网络在深度学习时代是十分的work的,虽然AI安全问题一直没有解决,但是比较发 ...
 - 2.16 Linux挂载详解
			
前面讲过,Linux 系统中"一切皆文件",所有文件都放置在以根目录为树根的树形目录结构中.在 Linux 看来,任何硬件设备也都是文件,它们各有自己的一套文件系统(文件目录结构) ...