详解RabbitMQ高级特性之死信队列

详解RabbitMQ高级特性之死信队列

目录

死信队列

添加配置

常量类

声明队列和交换机并绑定二者关系

死信--消息过期

给队列设置TTL

编写生产消息代码

编写消费消息代码

观察现象

死信--消息超过队列最大长度

设置队列的最大长度

编写生产消息代码

编写消费消息代码

观察现象

死信--消息被拒绝

编写生产消息代码

编写消费消息代码

观察现象

面试题


死信队列

死信(dead message) 简单理解就是因为种种原因, ⽆法被消费的信息, 就是死信.
有死信, ⾃然就有死信队列. 当消息在⼀个队列中变成死信之后,它能被重新被发送到另⼀个交换器
中,这个交换器就是DLX( Dead Letter Exchange ), 绑定DLX的队列, 就称为死信队列(Dead
Letter Queue,简称DLQ).

消息变成死信⼀般是由于以下⼏种情况:

1. 消息被拒绝( Basic.Reject/Basic.Nack ),并且设置 requeue 参数为 false.
2. 消息过期.
3. 队列达到最⼤⻓度.
添加配置
spring: application: name: rabbit-extensions-demo rabbitmq: addresses: amqp://study:[email protected]:5672/extension
常量类
public class Constants { //死信 public static final String NORMAL_QUEUE = "normal.queue"; public static final String NORMAL_EXCHANGE = "normal.exchange"; public static final String DL_QUEUE = "dl.queue"; public static final String DL_EXCHANGE= "dl.exchange"; }
声明队列和交换机并绑定二者关系
import org.springframework.amqp.core.*; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import rabbitextensionsdemo.constant.Constants; @Configuration public class DLConfig { //正常的交换机和队列 @Bean("normalQueue") public Queue normalQueue(){ return QueueBuilder.durable(Constants.NORMAL_QUEUE) .deadLetterExchange(Constants.DL_EXCHANGE) .deadLetterRoutingKey("dlx") .build(); } @Bean("normalExchange") public DirectExchange normalExchange(){ return ExchangeBuilder.directExchange(Constants.NORMAL_EXCHANGE).build(); } @Bean("normalBinding") public Binding normalBinding(@Qualifier("normalQueue") Queue queue, @Qualifier("normalExchange") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("normal").noargs(); } //死信交换机和队列 @Bean("dlQueue") public Queue dlQueue(){ return QueueBuilder.durable(Constants.DL_QUEUE).build(); } @Bean("dlExchange") public DirectExchange dlExchange(){ return ExchangeBuilder.directExchange(Constants.DL_EXCHANGE).build(); } @Bean("dlBinding") public Binding dlBinding(@Qualifier("dlQueue") Queue queue, @Qualifier("dlExchange") Exchange exchange){ return BindingBuilder.bind(queue).to(exchange).with("dlx").noargs(); } }
死信--消息过期
给队列设置TTL
编写生产消息代码
 @RequestMapping("/dl") public String dl() { System.out.println("dl..."); //发送普通消息 rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE, "normal", "dl test..."); System.out.printf("%tc 消息发送成功 \n", new Date()); return "消息发送成功"; }
编写消费消息代码
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import rabbitextensionsdemo.constant.Constants; import java.util.Date; @Component public class DLListener { @RabbitListener(queues = Constants.DL_QUEUE) public void dlHandMessage(Message message, Channel channel) throws Exception { //消费者逻辑 System.out.printf("[dl.queue] %tc 接收到消息: %s, deliveryTag: %d \n", new Date(), new String(message.getBody(),"UTF-8"), message.getMessageProperties().getDeliveryTag()); } }
观察现象

我们可以看到,消息在10秒后过期,从normal队列进入到了死信队列,消息进入到死信队列后被消费。

死信--消息超过队列最大长度
设置队列的最大长度
编写生产消息代码
 @RequestMapping("/dl") public String dl() { //测试队列长度 for (int i = 0; i < 20; i++) { rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE, "normal", "dl test..."+i); } return "消息发送成功"; }
编写消费消息代码
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import rabbitextensionsdemo.constant.Constants; import java.util.Date; @Component public class DLListener { @RabbitListener(queues = Constants.DL_QUEUE) public void dlHandMessage(Message message, Channel channel) throws Exception { //消费者逻辑 System.out.printf("[dl.queue] %tc 接收到消息: %s, deliveryTag: %d \n", new Date(), new String(message.getBody(),"UTF-8"), message.getMessageProperties().getDeliveryTag()); } }
观察现象

此时我们可以看到,给队列设置了最大长度为10,但是队列接收到了20条消息,就会导致前10条消息变成死信。

死信--消息被拒绝
编写生产消息代码
 @RequestMapping("/dl") public String dl() { System.out.println("dl..."); //发送普通消息 rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE, "normal", "dl test..."); System.out.printf("%tc 消息发送成功 \n", new Date()); return "消息发送成功"; }
编写消费消息代码
import com.rabbitmq.client.Channel; import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; import rabbitextensionsdemo.constant.Constants; @Component public class DLListener { @RabbitListener(queues = Constants.NORMAL_QUEUE) public void handMessage(Message message, Channel channel) throws Exception { long deliveryTag = message.getMessageProperties().getDeliveryTag(); try { //消费者逻辑 System.out.printf("[normal.queue]接收到消息: %s, deliveryTag: %d \n", new String(message.getBody(),"UTF-8"), message.getMessageProperties().getDeliveryTag()); //进行业务逻辑处理 System.out.println("业务逻辑处理"); int num = 3/0; System.out.println("业务处理完成"); //肯定确认 channel.basicAck(deliveryTag,false); } catch (Exception e) { //否定确认 channel.basicNack(deliveryTag, false, false); //requeue为false, 该消息成为死信 } } }
观察现象

可以看到,normal队列中的消息在被消费时因为发生了异常而执行到了拒绝消息的代码,而且设置了消息不重新入队,导致消息变成了死信,进而进入到了死信队列。

面试题

1.死信队列的概念

死信(Dead Letter)是消息队列中的⼀种特殊消息, 它指的是那些⽆法被正常消费或处理的消息. 在消息队列系统中, 如RabbitMQ, 死信队列⽤于存储这些死信消息。

2.死信的来源

1) 消息过期: 消息在队列中存活的时间超过了设定的TTL
2) 消息被拒绝: 消费者在处理消息时, 可能因为消息内容错误, 处理逻辑异常等原因拒绝处理该消息. 如果拒绝时指定不重新⼊队(requeue=false), 消息也会成为死信.
3) 队列满了: 当队列达到最⼤⻓度, ⽆法再容纳新的消息时, 新来的消息会被处理为死信.

3.死信的应用场景 

对于RabbitMQ来说, 死信队列是⼀个⾮常有⽤的特性. 它可以处理异常情况下,消息不能够被消费者正确消费⽽被置⼊死信队列中的情况, 应⽤程序可以通过消费这个死信队列中的内容来分析当时所遇到的异常情况, 进⽽可以改善和优化系统.

⽐如: ⽤⼾⽀付订单之后, ⽀付系统会给订单系统返回当前订单的⽀付状态
为了保证⽀付信息不丢失, 需要使⽤到死信队列机制. 当消息消费异常时, 将消息投⼊到死信队列中, 由订单系统的其他消费者来监听这个队列, 并对数据进⾏处理(⽐如发送⼯单等,进⾏⼈⼯确认).

场景的应⽤场景还有:

• 消息重试:将死信消息重新发送到原队列或另⼀个队列进⾏重试处理.
• 消息丢弃:直接丢弃这些⽆法处理的消息,以避免它们占⽤系统资源.
• ⽇志收集:将死信消息作为⽇志收集起来,⽤于后续分析和问题定位.

Read more

手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人

手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人

手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人 当前版本 OpenClaw(2026.2.22-2)已内置飞书插件,无需额外安装。 你有没有想过,在飞书里直接跟 AI 对话,就像跟同事聊天一样自然? 今天这篇文章,带你从零开始,用 OpenClaw 搭建一个飞书 AI 机器人。全程命令行操作,10 分钟搞定。 一、准备工作 1.1 安装 Node.js(版本 ≥ 22) OpenClaw 依赖 Node.js 运行,首先确保你的 Node 版本不低于 22。 推荐使用 nvm 管理 Node

By Ne0inhk

【无人机3D路径规划】基于改进蝙蝠优化算法的无人机3D路径规划研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。 🍎 往期回顾关注个人主页:Matlab科研工作室  👇 关注我领取海量matlab电子书和数学建模资料  🍊个人信条:格物致知,完整Matlab代码获取及仿真咨询内容私信。 🔥 内容介绍  一、引言 在当今科技飞速发展的时代,无人机在众多领域得到了广泛应用,从物流配送、农业监测到航空测绘等。在这些应用场景中,无人机需要在三维空间中规划出一条安全、高效的飞行路径,以完成各种任务。传统的路径规划算法在处理复杂的 3D 环境时,往往存在收敛速度慢、易陷入局部最优等问题。蝙蝠优化算法(Bat Algorithm,BA)作为一种新兴的智能优化算法,模拟了蝙蝠的回声定位行为,为解决此类问题提供了新的思路。然而,标准的蝙蝠优化算法也有其局限性,因此本文聚焦于基于改进蝙蝠优化算法的无人机 3D 路径规划研究,旨在提升路径规划的性能。 二、蝙蝠优化算法基础 1. 蝙蝠回声定位模拟:蝙蝠在飞行过程中通过发出超声波,并根据回声来感知周围环

By Ne0inhk
EnvPilot:一款基于 Rust 的跨平台环境变量神器,一键搞定 Windows/Linux 环境配置!

EnvPilot:一款基于 Rust 的跨平台环境变量神器,一键搞定 Windows/Linux 环境配置!

文章目录 * 1. 项目介绍🎯 * 1.1. 什么是 EnvPilot? * 1.2. 为什么选择 EnvPilot? * 2. 核心优势:四大痛点全部解决!💪 * ✅ 痛点一:添加不生效?已修复! * ✅ 痛点二:删除删不掉?已修复! * ✅ 痛点三:PATH 清理失效?已修复! * ✅ 痛点四:误操作无法恢复?已解决! * 3. 支持的开发环境🛠️ * 4. 详细使用教程📖 * 4.1. Windows 平台使用教程 * 1️⃣ 下载安装 * 2️⃣ 配置环境变量 * 3️⃣ 清除环境变量 * 4.2. Linux 平台使用教程 * 1️⃣ 从源码编译 * 2️⃣ 配置环境变量 * 3️

By Ne0inhk

RocketMQ与RabbitMQ全方位深度对比分析

文章目录 * 前言 * 一、设计基因:根本差异的源头 * 1.1 出身与定位 * 1.2 设计哲学的本质差异 * 1.3 核心优势领域 * 二、架构与消息模型:从底层机制看差异 * 2.1 存储引擎差异 * RocketMQ:CommitLog + ConsumeQueue * RabbitMQ:队列 + Mnesia * 2.2 核心组件对比 * 2.3 消息路由机制对比 * RocketMQ:Topic + Tag 二级过滤 * RabbitMQ:Exchange 多样化路由 * 2.4 消息流转完整链路 * RocketMQ 消息流转 * RabbitMQ 消息流转 * 三、集群架构与高可用机制 * 3.1

By Ne0inhk