解决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,…...

2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
是否存在路径(FIFOBB算法)
题目描述 一个具有 n 个顶点e条边的无向图,该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序,确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数,分别表示n 和 e 的值(1…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...