当前位置: 首页 > news >正文

解决RabbitMQ设置TTL过期后不进入死信队列

解决RabbitMQ设置TTL过期后不进入死信队列

  • 问题发现
  • 问题解决
    • 方法一:只监听死信队列,在死信队列里面处理业务逻辑
    • 方法二:改为自动确认模式

问题发现

最近再学习RabbitMQ过程中,看到关于死信队列内容:

来自队列的消息可以是 “死信”,这意味着当以下四个事件中的任何一个发生时,这些消息将被重新发布到 Exchange

  1. 使用 basic.rejectbasic.nackrequeue 参数设置为 false 的使用者否定该消息
  2. 消息由于每条消息的 TTL 而过期
  3. 队列超出了长度限制
  4. 消息返回到 quorum 队列的次数超过了 delivery-limit 的次数。

再模拟TTL过期时遇到的疑惑,特此记录下来,示例代码如下:
先设置为手动应答模式:

#手动应答
spring.rabbitmq.listener.simple.acknowledge-mode = manual

绑定队列,示例代码如下:

@Configuration
public class MQConfig {/*** 死信队列* @return*/@Beanpublic Queue deadQueue(){return new Queue("dead_queue");}/*** 死信队列交换机* @return*/@Beanpublic DirectExchange deadExchange(){return new DirectExchange("dead.exchange");}/*** 死信队列和死信交换机绑定* @return*/@Beanpublic Binding deadBinding(){return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("dead");}/*** 普通队列* @return*/@Beanpublic Queue queue(){
//          方法一  
//        Queue normalQueue = new Queue("normal_queue");
//        normalQueue.addArgument("x-dead-letter-exchange", "dead.exchange"); // 死信队列
//        normalQueue.addArgument("x-dead-letter-routing-key", "dead"); // 死信队列routingKey
//        normalQueue.addArgument("x-message-ttl", 10000); // 死信队列routingKey
//        方法二return QueueBuilder.durable("normal_queue").deadLetterExchange("dead.exchange").deadLetterRoutingKey("dead").ttl(10000).build();}/*** 普通交换机* @return*/@Beanpublic DirectExchange normalExchange(){return new DirectExchange("normal.exchange");}/*** 普通队列和普通交换机绑定* @return*/@Beanpublic Binding binding(){return BindingBuilder.bind(queue()).to(normalExchange()).with("normal");}
}

监听普通队列消费方,示例代码如下:

@Component
@RabbitListener(queues = "normal_queue")
public class MQReceiver {private static final Logger log = LoggerFactory.getLogger(MQReceiver.class);@RabbitHandlerpublic void receive(String msg, Message message, Channel channel) throws IOException, InterruptedException {log.info("收到消息:"+msg);}
}

监听死信队列消费方,示例代码如下:

@Component
@RabbitListener(queues = "dead_queue")
public class MQReceiver2 {private static final Logger log = LoggerFactory.getLogger(MQReceiver2.class);@RabbitHandlerpublic void receive(String msg, Message message, Channel channel) throws IOException {log.info("死信队列收到消息:{}",msg);// 参数一:当前消息标签,参数二:true该条消息已经之前所有未消费设置为已消费,false只确认当前消息channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);}
}

发送方,向普通队列发送消息,示例代码如下:

@Component
public class MQSender {private static final Logger log = LoggerFactory.getLogger(MQSender.class);@Autowiredprivate RabbitTemplate template;public void send() throws UnsupportedEncodingException {String msg = "hello world";log.info("发送消息:"+msg);template.convertAndSend("normal.exchange", "normal", msg);}
}

执行结果如图:

在这里插入图片描述
时间到了后,死信队列长时间未收到消息,消息一直在普通队列中,如图所示:

在这里插入图片描述

然后开始百度,网上很多都说什么配置不对啥的,还有说队列的预取值太大导致的问题(扯犊子呢),反正就是没有找到一个合理的解释。

然后吃了个饭回来,发现RabbitMQ报了一个长时间未收到消息确认的错误(大概意思就是说ACK消息确认超时时间为18000毫秒也就是30分钟),原来RabbitMQ一直在等待消息确认,所以一直被持有,当普通队列挂了(重启后),被释放,进入死信队列。

PRECONDITION_FAILED - delivery acknowledgement on channel 1 timed out. Timeout value used: 1800000 ms. This timeout value can be configured, see consumers doc guide to learn more

在这里插入图片描述
这下知道为什么不进入死信队列的原因了。新的问题又来了,如果我手动确认或者拒绝了,那不就达不到TTL过期的效果了吗?

问题解决

方法一:只监听死信队列,在死信队列里面处理业务逻辑

这个方法是参考众多文章比较常见的一个做法,但是个人感觉与我理解的TTL有偏差(应该是在普通队列中处理超时的一种补偿机制,如果只监听死信队列,那就完全不需要在配置时普通队列里面定义死信队列,虽然这种做法可以解决业务问题),另一方面官方也有提到:

消息可以在写入套接字之后过期,但在到达消费者之前过期。

示例代码如下:

@Configuration
public class MQConfig {/*** 死信队列* @return*/@Beanpublic Queue deadQueue(){return new Queue("dead_queue");}/*** 死信队列交换机* @return*/@Beanpublic DirectExchange deadExchange(){return new DirectExchange("dead.exchange");}/*** 死信队列和死信交换机绑定* @return*/@Beanpublic Binding deadBinding(){return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("dead");}/*** 普通队列* @return*/@Beanpublic Queue queue(){
//          方法一  
//        Queue normalQueue = new Queue("normal_queue");
//        normalQueue.addArgument("x-dead-letter-exchange", "dead.exchange"); // 死信队列
//        normalQueue.addArgument("x-dead-letter-routing-key", "dead"); // 死信队列routingKey
//        normalQueue.addArgument("x-message-ttl", 10000); // 死信队列routingKey
//        方法二return QueueBuilder.durable("normal_queue").deadLetterExchange("dead.exchange").deadLetterRoutingKey("dead").ttl(10000).build();}/*** 普通交换机* @return*/@Beanpublic DirectExchange normalExchange(){return new DirectExchange("normal.exchange");}/*** 普通队列和普通交换机绑定* @return*/@Beanpublic Binding binding(){return BindingBuilder.bind(queue()).to(normalExchange()).with("normal");}}

消费方只监听死信队列:

@Component
@RabbitListener(queues = "dead_queue")
public class MQReceiver2 {private static final Logger log = LoggerFactory.getLogger(MQReceiver2.class);@RabbitHandlerpublic void receive(String msg, Message message, Channel channel) throws IOException {log.info("死信队列收到消息:{}",msg);// 伪代码:判断订单状态,1支付成功,2支付超时
//        if(order.state == 1){
//            // 参数一:当前消息标签,参数二:true该条消息已经之前所有未消费设置为已消费,false只确认当前消息
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//        }else{
//            // todo 修改订单状态
//            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
//        }}
}

发送方代码如下:

@Component
public class MQSender {private static final Logger log = LoggerFactory.getLogger(MQSender.class);@Autowiredprivate RabbitTemplate template;public void send() throws UnsupportedEncodingException {String msg = "hello world";log.info("发送消息:"+msg);template.convertAndSend("normal.exchange", "normal", msg);}
}

调用send()方法,执行结果如图:

在这里插入图片描述
可以看到从发送时间到进入死信队列时间正好间隔10s。

方法二:改为自动确认模式

经过思考后,既然手动确认走不通,那不如试一试自动模式,我们在普通队列里面,模拟业务出现异常情况(如果只是单纯模拟业务超时,不会进入死信队列,直接就确认消费了)。

我们先把手动确认的配置删除或者修改为自动确认,示例代码如下:

#spring.rabbitmq.listener.simple.acknowledge-mode = auto

发送方代码和配置的代码就不重复展示了(参考之前示例),消费方示例代码如下:

@Component
@RabbitListener(queues = "normal_queue")
public class MQReceiver {private static final Logger log = LoggerFactory.getLogger(MQReceiver.class);@RabbitHandlerpublic void receive(String msg) throws IOException, InterruptedException {log.info("收到消息:"+msg);throw new RuntimeException();}
}
@Component
@RabbitListener(queues = "dead_queue")
public class MQReceiver2 {private static final Logger log = LoggerFactory.getLogger(MQReceiver2.class);@RabbitHandlerpublic void receive(String msg) throws IOException {log.info("死信队列收到消息:{}",msg);// 伪代码:判断订单状态,1支付成功,2支付超时
//        if(order.state == 1){
//            // 参数一:当前消息标签,参数二:true该条消息已经之前所有未消费设置为已消费,false只确认当前消息
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//        }else{
//            // todo 修改订单状态
//            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
//        }}
}

调用send()方法,执行结果如图:

在这里插入图片描述
我们可以看到第一次进入普通队列时间和最后一次报错进入死信队列的时间,正好间隔10s。但是这中间会重复发起N次,不合理是时长,可能会导致资源消耗过高,但这又属于另外一个问题了。

相关文章:

解决RabbitMQ设置TTL过期后不进入死信队列

解决RabbitMQ设置TTL过期后不进入死信队列 问题发现问题解决方法一:只监听死信队列,在死信队列里面处理业务逻辑方法二:改为自动确认模式 问题发现 最近再学习RabbitMQ过程中,看到关于死信队列内容: 来自队列的消息可…...

【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_kernel() 源码分析

【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_kernel 源码分析 系列文章汇总:《鸿蒙OH-v5.0源码分析之 Uboot+Kernel 部分】000 - 文章链接汇总》 本文链接:《【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_ke…...

EndnoteX9安装及使用教程

EndnoteX9安装及使用教程 一、EndNote安装 1.1 下载 这里提供一个下载链接: 链接:https://pan.baidu.com/s/1RlGJksQ67YDIhz4tBmph6Q 提取码:5210 解压完成后,如下所示: 1.2 安装 双击右键进行安装 安装比较简单…...

SQL:子查询

子查询是SQL中强大的功能之一,它允许在一个查询内部嵌套另一个查询,以便处理更复杂的逻辑或数据检索需求。子查询可以用在SELECT、FROM、WHERE、HAVING、IN、ANY、ALL等子句中,根据使用场景和目的的不同,子查询可以分为多种类型。…...

C语言刷题日记(附详解)(5)

一、选填部分 第一题: 下面代码在64位系统下的输出为( ) void print_array(int arr[]) {int n sizeof(arr) / sizeof(arr[0]);for (int i 0; i < n; i)printf("%d", arr[i]); } int main() {int arr[] { 1,2,3,4,5 };print_array(arr);return 0; } A . 1…...

开源加密软件简介

开源加密软件是指源代码公开、可供任何人查看、修改和分发的加密软件。这类软件通常由社区维护&#xff0c;具有高度的透明性和安全性。 1. GnuPG (GNU Privacy Guard) 简介&#xff1a;GnuPG是一种基于OpenPGP标准的加密和签名工具&#xff0c;广泛应用于电子邮件加密和文件…...

【C++学习】 IO 流揭秘:高效数据读写的最佳实践

✨ 今朝有酒今朝醉&#xff0c;明日愁来明日愁 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3f…...

C#使用TCP-S7协议读写西门子PLC(五)-测试程序

上面四篇我们进行封装连接PLC以及读写PLC内存地址表 C#使用TCP-S7协议读写西门子PLC(一)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(二)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(三)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(四)-CSDN博客 这里我们进行测试操作 西门子PLC-…...

经验——IMX6UL的uboot无法ping主机或Ubuntu

主要描述可能的方向&#xff0c;不涉具体过程&#xff0c;详细操作可以查阅网上相关教程 跟随正点原子教程测试以太网端口时&#xff0c;即便按照步骤多次尝试也无法ping通&#xff0c;后补充了些许网络工程基础知识解决了这个问题。 uboot无法ping主机或Ubuntu有多种可能&…...

AUTOSAR-规范文档版本

https://www.autosar.org/standards 2024年11月&#xff08;R24-11&#xff09; Foundation&#xff08;FO) // TODO Classic&#xff08;CP&#xff09; // TODO Adaptive&#xff08;AP&#xff09; // TODO 2023年11月&#xff08;R23-11&#xff09; Foundation&a…...

网络(四)——HTTP协议

文章目录 认识urlurlencode和urldecodeHTTP协议格式HTTP的方法HTTP的状态码HTTP常见Header 虽然应用层的协议是由人为规定的&#xff0c;但是已经有大佬们定义了一些现成的&#xff0c;又非常好用的应用层协议&#xff0c;供我们直接参考使用. HTTP(超文本传输协议)就是其中之一…...

comfyui中报错 Cmd(‘git‘) failed due to: exit code(128) 如何解决

&#x1f388;背景 comfyui今天在安装插件的过程中&#xff0c;发现有个插件第一次安装失败后&#xff0c;再次安装就开始报错了&#xff0c;提示&#xff1a; ComfyUI-Inpaint-CropAndStitch install failed: Bad Request 截图如下&#xff1a; 看下后台的报错&#xff1a; …...

测试-Gatling 与性能测试

Gatling 与性能测试详解 一、什么是性能测试&#xff1f; 性能测试是一种软件测试类型&#xff0c;旨在评估系统在负载下的响应时间、吞吐量和资源利用率等性能指标。通过性能测试&#xff0c;开发者和运维团队能够识别出系统的瓶颈、优化系统性能&#xff0c;并确保其在实际…...

ESRGAN——老旧照片、视频帧的修复和增强,提高图像的分辨率

ESRGAN&#xff08;Enhanced Super-Resolution GAN&#xff09;&#xff1a;用于提高图像的分辨率&#xff0c;将低质量图像升级为高分辨率版本&#xff0c;常用于老旧照片、视频帧的修复和增强。 一、ESRGAN 介绍 1.1 背景 超分辨率问题是计算机视觉中的一个重要研究领域&a…...

跨界融合:EasyDSS+无人机视频直播推流技术助力行业多场景应用

随着科技的飞速发展&#xff0c;无人机技术与流媒体技术的结合正逐步改变着多个行业的运作模式。其中&#xff0c;EasyDSS互联网视频云服务与无人机视频直播推流技术的结合&#xff0c;更是为警务安防、赛事直播、农业监测等多个领域带来了前所未有的变革。本文将深入探讨EasyD…...

Linux实操笔记2 Ubuntu安装Nginx的不同方法

今天来了解Ubuntu或者说Linux系统安装Nginx的几种办法。包括从Ubuntu的库安装到官方源码编译安装。 一、Nginx是什么&#xff1f; 以下是来自Nginx中文文档的内容。 Nginx 是一个高性能的 Web 和反向代理服务器, 它具有有很多非常优越的特性: 作为 Web 服务器&#xff1a;相比…...

QCustomPlot笔记(一)

文章目录 简介将帮助文档添加到Qt Creator中编译共享库cmake工程编译提示ui_mainwindow.h找不到qcustomplot.h文件 环境:windowsQt Creator 10.0.1cmake 简介 QT中用于绘制曲线的第三方工具 下载地址&#xff1a;https://www.qcustomplot.com/index.php/download 第一个压缩…...

【机器学习】多模态AI——融合多种数据源的智能系统

随着人工智能的快速发展&#xff0c;单一模态&#xff08;如文本、图像或语音&#xff09;已经不能满足复杂任务的需求。多模态AI&#xff08;Multimodal AI&#xff09;通过结合多种数据源&#xff08;如文本、图像、音频等&#xff09;来提升模型的智能和表现&#xff0c;适用…...

QT学习与数据库连接

1.基础 1. 安装最后一个非在线版本 5.14, 没有的话联系我 新建一个.cpp文件 #include <QApplication> #include <QLabel> #include <QLineEdit> #include <QPushButton> #include <QHBoxLayout> #include <QVBoxLayout> #include <Q…...

泛读笔记:从Word2Vec到BERT

自然语言处理(NLP)模型的发展历史 1.统计方法时期&#xff1a;使用贝叶斯方法、隐马尔可夫模型、概率模型等传统统计方法 2.机器学习时期&#xff1a;支持向量机(SVM)、决策树模型、随机森林、朴素贝叶斯等传统机器学习方法 3.深度学习革命&#xff1a;各种新的深度学习模型&am…...

Claude in Excel:原生集成的AI表格协作者

1. 项目概述&#xff1a;这不是插件&#xff0c;是Excel里长出来的AI同事“Claude in Excel”这个标题刚看到时&#xff0c;我下意识点开几个技术社区翻了一圈&#xff0c;发现多数人第一反应是&#xff1a;“又一个AI插件&#xff1f;”——其实完全不是。它根本没走传统Offic…...

无机布防火卷帘门价格怎么算?按尺寸定制,按需报价

无机布防火卷帘门作为建筑防火分区的核心设备&#xff0c;价格一直是工程采购的关注重点。很多用户在询价时&#xff0c;会发现不同厂家的报价差异较大&#xff0c;这是因为无机布防火卷帘门的价格并非按统一单价计算&#xff0c;而是完全根据项目的实际需求定制化核算。 &…...

保姆级教程:在ArcGIS Pro插件中集成你的自定义工具箱(以‘消除重复要素’为例)

从脚本到按钮&#xff1a;ArcGIS Pro插件开发实战指南 在GIS日常工作中&#xff0c;我们常常会遇到一些重复性的数据处理任务。比如数据质检环节的"消除重复要素"操作&#xff0c;虽然可以通过Python脚本实现&#xff0c;但每次都需要打开IDE或Python窗口执行代码&am…...

告别命令行!用Python脚本批量管理Docker容器,效率提升不止一点点

告别命令行&#xff01;用Python脚本批量管理Docker容器&#xff0c;效率提升不止一点点每次在终端敲入docker ps、docker stop、docker rm时&#xff0c;你是否想过——当容器数量超过两位数&#xff0c;这种重复劳动是否在消耗你的生命&#xff1f;去年我们团队在迁移微服务架…...

深度学习从心电信号中解码呼吸频率:原理、实现与临床价值

1. 项目概述&#xff1a;从心电信号中“听”到呼吸声呼吸频率&#xff0c;这个我们每分钟都在进行却很少被精确量化的生命体征&#xff0c;在临床医学中扮演着至关重要的角色。它不仅是评估呼吸系统功能的直接指标&#xff0c;更是反映全身代谢、循环乃至神经系统状态的“窗口”…...

用Python+OpenCV手把手实现Prewitt边缘检测(附完整代码与效果对比图)

用PythonOpenCV手把手实现Prewitt边缘检测&#xff08;附完整代码与效果对比图&#xff09; 边缘检测是计算机视觉中最基础也最关键的预处理步骤之一。想象一下&#xff0c;当你需要让计算机"看清"一张照片中的物体轮廓时&#xff0c;边缘检测算法就是它的"视觉…...

Hirschmann RS20-0800M4M4SDAE工业以太网交换机

Hirschmann RS20-0800M4M4SDAE 工业以太网交换机产品特点&#xff1a;端口配置&#xff1a;共8个端口&#xff0c;含6个RJ45电口和2个ST光纤接口。端口速率&#xff1a;所有端口均为100Mbps快速以太网。光纤类型&#xff1a;2个光纤端口为多模、ST接头。管理类型&#xff1a;二…...

第二周(第12周)

1.单电源供电的二阶低通滤波器2.功率放大电路...

‌2026智慧校园规划必读:如何在预算吃紧下选到高性价比方案‌

✅作者简介&#xff1a;合肥自友科技 &#x1f4cc;核心产品&#xff1a;智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…...

别再死记硬背了!用5个生活化比喻彻底搞懂Linux进程的fork、exec和wait

别再死记硬背了&#xff01;用5个生活化比喻彻底搞懂Linux进程的fork、exec和wait想象你正在厨房准备一顿大餐。菜谱上写着"切菜"、"炒菜"、"装盘"等步骤&#xff0c;但突然发现需要同时处理多道菜品——这时候&#xff0c;你会本能地让家人分工…...