解决RabbitMQ设置x-max-length队列最大长度后不进入死信队列
解决RabbitMQ设置x-max-length队列最大长度后不进入死信队列
- 问题发现
- 问题解决
- 方法一:只监听死信队列,在死信队列里面处理业务逻辑
- 方法二:修改预取值
问题发现
最近再学习RabbitMQ过程中,看到关于死信队列内容:
来自队列的消息可以是 “死信”,这意味着当以下四个事件中的任何一个发生时,这些消息将被重新发布到 Exchange。
- 使用
basic.reject
或basic.nack
且 requeue 参数设置为false
的使用者否定该消息- 消息由于每条消息的 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-max-length", 5);//设置队列最大长度
// normalQueue.addArgument("x-overflow","reject-publish");//最近发布的消息将被丢弃
// 方法二return QueueBuilder.durable("normal_queue").deadLetterExchange("dead.exchange").deadLetterRoutingKey("dead").maxLength(5) // 设置队列最大长度为5.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) {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) {log.info("死信队列收到消息:{}",msg);}
}
发送方发送10条消息,示例代码如下:
@Component
public class MQSender {@Autowiredprivate RabbitTemplate template;public void send() {for (int i = 0; i < 10; i++) {String msg = "hello world_"+i;template.convertAndSend("normal.exchange", "normal", msg);}}
}
然后调用send()
方法,执行结果如图:
按道理会将队列前面的5条消息进入死信队列,然后剩下的五条消息正常消费才对,我们检查一下队列是否设置成功,如图所示
设置没有问题,那就很懵逼了。。。
问题解决
我们先在页面上向普通队交换机发送10条消息,然后查看它的状态,如图所示:
超过5条消息就会放入死信队列中,如图所示:
然后再看一下,默认行为是从队列前面删除或死信消息,如图所示:
我们可以看到普通队列存放的是最后5条消息,前面的5条消息进入死信队列。也就是说,再没有进入普通消费者之前会将队列前面删除或死信消息(进入消费者之前将消息进行分配)。
方法一:只监听死信队列,在死信队列里面处理业务逻辑
这种做法也是网上大多数文章的一种处理方法(另外一种情况就是进入普通消费者,还没被消费完的情况下,消费者挂了,然后队列就会重新分配,将从队列前面删除或者进入死信队列),如图所示:
但是这些做法都是基于没有普通消费者监听的情况下进行的,感觉和我理解的略有偏差(应该是再有普通消费者监听和死信队列监听的情况下,发送消息时会对消息进行分配处理)。
发送方代码和配置的代码就不重复展示了(参考之前示例),监听死信队列(自动确认模式和手动确认模式都一样),示例代码如下:
@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);channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}
}
调用send()
方法,执行结果如图:
然后再死信队列里面处理业务逻辑即可。
方法二:修改预取值
再监听普通队列时抛异常或者手动拒绝重新进入队列,这两种方式都不能达到我想要的效果,我发现只要有普通消费方监听就会进入消费,然后我就在想是不是因为预期值的问题导致,可能我消息数量太少了,然后默认预期值太高导致消息直接进入消费,然后将预取值改为1,自动确认模式,示例代码如下:
spring.rabbitmq.listener.simple.prefetch=1
发送方代码和配置的代码就不重复展示了(参考之前示例),监听普通队列,示例代码如下:
@Component
@RabbitListener(queues = "normal_queue")
public class MQReceiver {private static final Logger log = LoggerFactory.getLogger(MQReceiver.class);@RabbitHandlerpublic void receive(String msg) throws InterruptedException {log.info("收到消息:"+msg);// 模拟业务处理队列等待场景Thread.sleep(10000);}
}
监听死信队列,示例代码如下:
@Component
@RabbitListener(queues = "dead_queue")
public class MQReceiver2 {private static final Logger log = LoggerFactory.getLogger(MQReceiver2.class);@RabbitHandlerpublic void receive(String msg) {log.info("死信队列收到消息:{}",msg);}
}
调用send()
方法,执行结果如图:
当然有的时候也不一定完全按照你设置的最大长度进入死信队列,有的时候消费速度太快(队列的第一个已经被消费了的情况),得看实际情况,至少可以确保再设置了大于队列最大长度时是可以正常进入死信队列的。归根结底:消息数量太少了。
另外我们再来介绍一下溢出方式,一般默认情况下溢出方式为:drop-head
(从队列前面删除或者进入死信队列),除此之外还有两种:
reject-publish
和reject-publish-dlx
:最近发布的消息将被丢弃。reject-publish
和reject-publish-dlx
之间的区别在于reject-publish-dlx
也是死信拒绝消息。
将配置的溢出模式改为reject-publish
,示例代码如下:
@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-max-length", 5);//设置队列最大长度
// normalQueue.addArgument("x-overflow","reject-publish");//最近发布的消息将被丢弃
// 方法二return QueueBuilder.durable("normal_queue").deadLetterExchange("dead.exchange").deadLetterRoutingKey("dead").maxLength(5) // 设置队列最大长度为5.overflow(QueueBuilder.Overflow.dropHead).build();}/*** 普通交换机* @return*/@Beanpublic DirectExchange normalExchange(){return new DirectExchange("normal.exchange");}/*** 普通队列和普通交换机绑定* @return*/@Beanpublic Binding binding(){return BindingBuilder.bind(queue()).to(normalExchange()).with("normal");}
}
重新创建队列,调用send()
方法,如图所示:
由图可以,死信队列并不会受到消息。
然后我们再来看看将溢出模式设置为reject-publish-dlx
(QueueBuilder.Overflow没有该参数,手动定义),示例代码如下:
@Configuration
public class MQConfig {//忽略死信队列创建和绑定过程,参考前面示例.../*** 普通队列* @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"); // 死信队列routingKeynormalQueue.addArgument("x-max-length", 5);//设置队列最大长度normalQueue.addArgument("x-overflow","reject-publish-dlx"); //最近发布的消息进入死信队列return normalQueue;}//忽略普通队列创建过程,参考前面示例...
}
重新创建队列,调用send()
方法,如图所示:
如果你有更好的方法或者不同的理解,欢迎评论区交流。
相关文章:

解决RabbitMQ设置x-max-length队列最大长度后不进入死信队列
解决RabbitMQ设置x-max-length队列最大长度后不进入死信队列 问题发现问题解决方法一:只监听死信队列,在死信队列里面处理业务逻辑方法二:修改预取值 问题发现 最近再学习RabbitMQ过程中,看到关于死信队列内容: 来自队…...

【解决】chrome 谷歌浏览器,鼠标点击任何区域都是 Input 输入框的状态,能看到输入的光标
chrome 谷歌浏览器,鼠标点击任何区域都是 Input 输入框的状态,能看到输入的光标 今天打开电脑的时候,网页中任何文本的地方,只要鼠标点击,就会出现一个输入的光标,无论在哪个站点哪个页面都是如此。 我知道…...
使用python操作数据库
文章目录 一、问题背景二、安装python三、代码示例四、总结 一、问题背景 在日常开发过程中,随着项目进展和业务功能的迭代,我们需要对数据库的表结构进行修改,向部分表中追加字段,并对追加后的字段进行数据填充。但是如果需要追加…...

[Redis] 渐进式遍历+使用jedis操作Redis+使用Spring操作Redis
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...

排序----数据结构
Comparable Integer Double 默认情况下都是按照升序排列的 string 按照字母再ASCII码表中对应的数字升序进行排列 冒泡排序 时间复杂度O(x^2) 选择排序 时间复杂度O(x^2) 插入排序 时间复杂度O(x^2) 希尔排序 时间复杂度O(x) 归并排序 时间复杂度O(nlogn) 快速排序...

Crack道路裂缝检测数据集——目标检测数据集
【Crack道路裂缝检测数据集】共3684张。 目标检测数据集,标注文件为YOLO适用的txt格式。已划分为训练、验证集。 图片分辨率:224*224 类别:crack Crack道路裂缝检测数据集 数据集描述 该数据集是一个专门用于训练和评估基于YOLO࿰…...

10.3拉普拉斯金字塔
实验原理 拉普拉斯金字塔(Laplacian Pyramid)是一种图像表示方法,常被用于图像处理和计算机视觉领域。它是基于高斯金字塔的一种变换形式,主要用于图像融合、图像金字塔的构建等场景。下面简要介绍拉普拉斯金字塔的基本原理。 高…...

redis为什么不使用一致性hash
Redis节点间通信时,心跳包会携带节点的所有槽信息,它能以幂等方式来更新配置。如果采用 16384 个插槽,占空间 2KB (16384/8);如果采用 65536 个插槽,占空间 8KB (65536/8)。 今天我们聊个知识点为什么Redis使用哈希槽而不是一致性…...
Vue.js与Flask/Django后端配合
Vue.js与Flask/Django后端配合 在现代Web开发领域,前后端分离已成为一种流行的架构模式。Vue.js作为一款轻量级、高性能的前端框架,与Flask或Django这样的后端框架相结合,可以构建出强大且可扩展的Web应用。本文将详细介绍如何将Vue.js与Fla…...

ESP32 入门笔记02: ESP32-C3 系列( 芯片ESP32-C3FN4) (ESP-IDF + VSCode)
ESP32-C3 系列的 芯片 / 模组 / 开发板 ESP32-C3-DevKitM-1是乐鑫一款搭载 ESP32-C3-MINI-1 或 ESP32-C3-MINI-1U 模组的入门级开发板(内置 ESP32-C3FH4 或 ESP32-C3FN4 芯片)。 板上模组大部分管脚均已引出至两侧排针,可根据开发实际需求&a…...
Vue主题色实现
主题色实现 情境 配置平台支持多个主题色的选择,用户可通过在配置平台选择项目主题色。前端项目在骨架屏加载页面获取配置信息,设置项目主题色,实现同个项目不同主题色渲染的需求 实现 1.定义主题色变量 不同主题色根据不同js文件划分定…...

ChartLlama: A Multimodal LLM for Chart Understanding and Generation论文阅读
原文链接:https://arxiv.org/abs/2311.16483 代码与数据集:https://tingxueronghua.github.io/ChartLlama/ 本文启发:文章提出利用GPT-4合成大量图表数据,这些数据包含各种图表类型,包含丰富的instruction data。然后…...
ByteCinema(1):用户的登录注册
文章目录 主要功能生成图形验证码redis滑动窗口操作限流0.限流设计的必要性1.原理2.代码(邮箱发验证码为例)3. 问题与解决高并发环境下redis操作的原子性过时数据的积累 续约token实现长期登录0.设计的出发点1.前置知识:JWT什么是 JWT?JWT 的…...
电力电网电线变电站输电线绝缘子无人机类数据集/农业植物病虫害类数据集/光伏板/工程煤矿矿场类数据集/道路类数据集
电力电网电线变电站输电线红外缺陷类数据集 传送门链接: 1.电线覆盖物检测数据集 气球风筝鸟巢 1300张 voc yol-CSDN博客 2.变电站可见光缺陷数据集数据集包含8376张巡检图像,带xml标签,共包含17类巡检标签!具体缺陷分类见下图!…...
深度学习之表示学习 - 引言篇
序言 在数据爆炸的今天,如何从纷繁复杂的信息中抽取有价值的知识,成为了人工智能领域亟待解决的核心问题。深度学习,作为机器学习的一个重要分支,以其强大的特征表示能力和自动化学习特性,引领了这场数据革命的浪潮。…...

Linux驱动开发 ——架构体系
只读存储器(ROM) 1.作用 这是一种非易失性存储器,用于永久存储数据和程序。与随机存取存储器(RAM)不同,ROM中的数据在断电后不会丢失,通常用于存储固件和系统启动程序。它的内容在制造时或通过…...
Django一分钟:lookupAPI详解,使用django orm生成高效的WHERE子句
一、Lookup API概述 Lookup API是Django用于构建数据库查询WHERE子句的API。 Lookup API的核心包含两部分: RegisterLookupMixin:为子类提供注册lookup的方法Query Expression API:一个接口,规定了可以被注册为lookup的类需要实…...

信息安全工程师(8)网络新安全目标与功能
前言 网络新安全目标与功能在当前的互联网环境中显得尤为重要,它们不仅反映了网络安全领域的最新发展趋势,也体现了对网络信息系统保护的不断加强。 一、网络新安全目标 全面防护与动态应对: 目标:建立多层次、全方位的网络安全防…...
返利机器人在电商返利系统中的负载均衡实现
返利机器人在电商返利系统中的负载均衡实现 大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!今天我们来聊一聊如何在电商返利系统中实现返利机器人的负载均衡,尤其是在面对高并发和大量…...
MATLAB中typecast函数用法
目录 语法 说明 示例 将整数转换为相同存储大小的无符号整数 将 8 位整数转换为单精度 将 32 位整数转换为 8 位整数 将 8 位整数转换为 16 位整数 提示 typecast函数的功能是在不更改基础数据的情况下转换数据类型。 语法 Y typecast(X,type) 说明 Y typecast(X,…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...

企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...