RabbitMQ 消息确认机制

温馨提示:基于JDK17SpringBoot 2.1.8.RELEASE 版本,由于RabbitMQSpringBoot3+ 的配置项有所不同, 所以请严格按照该本版来使用,挖一坑:【后续会出一个SpringBoot3+版本的配置相关教程】

架构

概念

保证消息不丢失,可靠抵达,可以使用事务消息,性能下降250倍 为此引入确认机制

  • 生产者确认回调:publisher confirmCallback
  • 生产者退回回调:publisher returnCallback未投递到queue退回模式
  • 消费者确认:consumer ack确认机制

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方法需要传入两个参数:deliveryTagmultipledeliveryTag是消息的唯一标识,由 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

​ 消费者收到消息,成功处理发送 AckBroker

​ 消费者收到消息自动确认,但是无法确认消息是否被处理完成或者成功处理,需要手动开启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 消息确认机制的更多相关文章

  1. RabbitMQ消息确认机制

    文章目录 1. 事务机制2. Confirm模式2.1 生产者2.1.1 普通Confirm模式2.1.2 批量Confirm模式2.1.3 异步Confirm模式2.2 消费者3. 其他 消费者如何 ...

  2. RabbitMQ 消息确认机制

    消息确认机制 在之前异常处理部分就已经写了,对于consumer的异常退出导致消息丢失,可以时候consumer的消息确认机制.重复的就不说了,这里说一些不一样的. consumer的消息确认机制 当 ...

  3. RabbitMQ 消息确认机制以及lazy queue+ disk消息持久化

    一:Basic的一些属性,一些方法 1. 消费端的确认 自动确认: message出队列的时候就自动确认[broke] basicget... 手工确认: message出队列之后,要应用程序自己去确 ...

  4. springboot + rabbitmq 用了消息确认机制,感觉掉坑里了

    本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI ...

  5. SpringBoot(九)RabbitMQ安装及配置和使用,消息确认机制

    Windows下RabbitMQ安装及配置地址: https://blog.csdn.net/zhm3023/article/details/82217222RabbitMQ(四)订阅模式:https ...

  6. RabbitMQ学习第七章:消息确认机制之事务机制

    RabbitMQ消息确认机制之事务机制. RabbitMQ中,我们可以通过持久化数据 解决RabbitMQ服务器异常 的数据丢失问题. 问题:生产者将消息发送出去,消息到底有没有到达RabbitMQ服 ...

  7. RabbitMQ消息确认(发送确认,接收确认)

    前面几篇记录了收发消息的demo,今天记录下关于 消息确认方面的 问题. 下面是几个问题: 1.为什么要进行消息确认? 2.rabbitmq消息确认 机制是什么样的? 3.发送方如何确认消息发送成功? ...

  8. RabbitMQ学习笔记之五种模式及消息确认机制

    本文详细介绍简单模式Simple.工作模式Work.发布订阅模式Publish/Subscribe.Topic.Routing. Maven依赖引用 <dependencies> < ...

  9. 消息队列RabbitMQ(三):消息确认机制

    引言 RabbitMQ的模型是生产者发送信息到 Broker (代理),消费者从 Broker 中取出信息.但是生产者怎么知道消息是否真的发送到 Broker 中了呢?Broker 又怎么知道消息到底 ...

  10. (转)RabbitMQ消息队列(九):Publisher的消息确认机制

    在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...

随机推荐

  1. 暑假集训CSP提高模拟18

    \[暑假集训CSP提高模拟 \ 1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1+1 \] Very good problem, this make my news rotate. ...

  2. excel江湖异闻录--渣渣

    有朋友问过我,为什么要写这些,细细思量,一来我喜欢这个纯粹的江湖,二则向这些纯粹的高手.大神致敬,三是纪念一下自己学习EXCEL的历程. 其实,每一个篇章都有一个逻辑,只不过这个逻辑,不是按照实力的高 ...

  3. flops, params = profile(model, inputs=(x,))计算

    计算量:FLOPs,FLOP时指浮点运算次数,s是指秒,即每秒浮点运算次数的意思,考量一个网络模型的计算量的标准.参数量:Params,是指网络模型中需要训练的参数总数. flops(G) = flo ...

  4. 墨天轮专访TDengine陶建辉:坚持做难而正确的事,三次创业成就不悔人生

    导读: 时序数据库(Time Series Database)在最近几年被越来越多的用户接受并使用,并有广泛的应用场景.云原生时序数据库 TDengine 一直稳居墨天轮时序数据库榜首,其近期的海外发 ...

  5. docker-compose -- 创建 redis && mysql

    version: '3' services: nest-admin-web: image: buqiyuan/vue3-antdv-admin:stable container_name: nest- ...

  6. 使用AVX2指令集加速推荐系统MMR层余弦相似度计算

    原文:blog.fanscore.cn/a/62/ 1. 背景 前一段时间公司上线了一套Go实现的推荐系统,上线后发现MMR层虽然只有纯计算但耗时十分离谱,通过pprof定位问题所在之后进行了优化,虽 ...

  7. day04-常用DOS命令

    打开cmd的方式 开始-W-windows系统-命令提示符 win键+R键 鼠标在任意文件夹上, shift+鼠标右键 资源管理器的地址栏前面加cmd,然后回车 管理员方式运行:选择命令提示符右键以管 ...

  8. 从Windows 11 23H2升级至24H2后,Git操作提示文件所有权错误的3种有效解决方案

    从Windows 11 23H2升级至24H2后,Git操作提示文件所有权错误的3种有效解决方案 在升级至 Windows 11 24H2 后,使用 git add 等命令时,可能会遇到如下错误提示: ...

  9. 一些新奇的玩意【php篇--持续更新】

    人不进步就等于退步! 接触越多的人以及事就能学到更多的东西. 以下仅为本人记录的一些新奇的东西,不喜勿喷! 1.??运算符号,在新的项目中突然发现很多红线报错,还以为是错误!看了下,是??运算的问题, ...

  10. Ubuntu自动连接到虚拟专用网络

    在我们继续之前,我们所做的一个重要假设是,您已经拥有了虚拟专用网络客户端配置文件. 您可以通过以下链接在我们的上一指南中看到一个示例 Open虚拟专用网络 客户端配置文件: 在 CentOS 8/乌本 ...