一、什么是死信队列
消息无法被正常消费,被 broker 判定为失效消息 ,不会继续留在原队列等待重试,转而投递到预先绑定的队列,这个专门接收失效消息的队列就是死信队列。
消息变成死信的三种核心场景
- 消费者拒绝消息
basicNack/basicReject,并且设置requeue=false(不重新放回原队列); 手动业务异常,主动拒收消息,不重试。
- 消息超时(TTL 过期)
- 消息本身设置过期时间;
- 队列设置全局消息过期时间; 消息一直未被消费,超时自动沦为死信。
- 队列达到最大长度 队列堆积消息数量超过
x-max-length上限,队尾多余消息被丢弃转入死信。
死信队列不是特殊类型队列,只是普通队列,依靠**死信交换机 DLX(Dead Letter Exchange)**实现路由转发。
二、核心组件:DLX 死信交换机
- 给原业务队列 配置两个参数:
x-dead-letter-exchange:指定死信交换机名称;x-dead-letter-routing-key:死信转发时使用的路由键(可选,默认沿用原消息 routingKey);
- 消息判定为死信后,RabbitMQ 会自动将这条消息重新发送到 DLX,DLX 根据路由键投递到绑定的死信队列。
整体流转链路:
生产者 → 普通交换机 → 业务队列 → 消费失败/超时/队列满
→ broker自动转发 → DLX死信交换机 → 死信队列DLQ
三、常用配置方式
方式 1:创建业务队列时绑定 DLX(最常用)
1)界面管理端配置
创建队列时添加 Arguments:
| 参数键 | 说明 |
|---|---|
x-dead-letter-exchange |
填写提前建好的死信交换机名称 |
x-dead-letter-routing-key |
死信路由键,自定义 |
x-message-ttl |
队列全局消息过期时间,单位 ms |
x-max-length |
队列最大消息条数 |
2)Java SpringBoot 代码示例
// 1.声明死信交换机
@Bean
public DirectExchange deadLetterExchange() {
return ExchangeBuilder.directExchange("dlx.exchange").durable(true).build();
}
// 2.声明死信队列
@Bean
public Queue deadLetterQueue() {
return QueueBuilder.durable("dlq.queue").build();
}
// 3.绑定死信交换机与死信队列
@Bean
public Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue())
.to(deadLetterExchange())
.with("dl.routing.key");
}
// 4.业务队列绑定死信交换机配置
@Bean
public Queue businessQueue() {
return QueueBuilder.durable("business.queue")
// 指定死信交换机
.withArgument("x-dead-letter-exchange", "dlx.exchange")
// 指定死信路由key
.withArgument("x-dead-letter-routing-key", "dl.routing.key")
// 可选:消息30秒超时未消费则进死信
.withArgument("x-message-ttl", 30000)
.build();
}
四、典型业务使用场景
1. 消费失败兜底处理
业务处理异常(数据库异常、第三方接口报错),不无限重试:
- 消费者捕获异常,执行
nack(requeue=false); - 消息进入死信队列,人工排查问题后重新投递。
2. 延迟队列(经典实现方案)
利用 TTL + 死信间接实现延迟:
- 消息先进入设置 TTL 的临时队列,无消费者;
- 消息超时自动转入 DLX,路由到目标业务队列;
- 消费者监听目标队列,达到定时消费效果。
局限:同一队列 TTL 必须一致,高精度延迟建议使用 RabbitMQ 延迟插件。
3. 防止消息无限堆积
限制业务队列最大消息数,流量突增打爆队列时溢出消息进入死信,避免服务雪崩。
五、死信队列标准运维方案
- 独立消费者监听死信队列 专门消费死信消息,记录日志、入库标记异常订单 / 任务,发送告警(钉钉 / 邮件);
- 死信重试策略
- 方案 A:人工修复问题后,将死信消息重新发送至原业务交换机;
- 方案 B:延时重试:死信接收后暂存 Redis,间隔一段时间重新投递,多次失败后永久归档;
- 死信消息留存 死信队列尽量不自动删除,保留数据用于问题溯源,定期归档清理。
六、容易踩坑的常见问题
- nack requeue=true 不会进死信 消息只是重回原队列头部,反复重试,极易死循环堆积,业务异常务必
requeue=false; - 死信交换机必须提前创建 如果业务队列配置的 DLX 不存在,死信消息会直接被丢弃;
- 死信消息携带原有属性 原消息的 headers、body、routingKey 都会保留,同时新增死信原因标识;
- 队列 TTL 和消息 TTL 优先级 消息发送时设置的
expiration优先级 > 队列配置的x-message-ttl。
七、简易工作流程示例
- 生产者发送消息到
business.exchange→business.queue; - 消费者处理报错,
channel.basicNack(deliveryTag, false, false); - RabbitMQ 检测到拒收且不重回队列,转发消息到
dlx.exchange; - DLX 根据路由键把消息投递至
dlq.queue; - 运维 / 专门消费者监听 dlq.queue,打印日志、触发告警,排查问题后重投。