什么是死信队列?RabbitMQ 如何实现延迟队列?

结论先行:死信指无法被正常消费的消息,把死信重新路由到指定交换机(DLX)后被消费的队列就是死信队列,常用于失败重试、异常告警与延迟任务。延迟队列在 RabbitMQ 中主流有两种实现:TTL + 死信队列模拟,或使用延迟消息插件 x-delayed-message,前者有队头阻塞缺陷,后者支持精确单条延迟。

一、消息成为死信的三种情况

触发条件说明
消费拒绝消费者 basicNack/basicReject 且 requeue=false
消息过期消息 TTL 到期未被消费
队列超限队列达到 x-max-length 或 x-max-length-bytes,最早消息被丢弃/转死信

二、延迟队列的两种实现

  • TTL + DLX 模拟:把业务队列的 x-dead-letter-exchange 指到真正消费的交换机,消息设置 TTL,过期后自动进入死信路由到业务队列;
  • 优点:不依赖插件、原理直观;缺点:同一队列存在队头阻塞——队头消息未过期会挡住后面先到期的消息,延迟精度差;
  • 插件方案:安装 rabbitmq_delayed_message_exchange 后声明 x-delayed-message 类型交换机,消息带 x-delay 头,到期后自动投递,支持单条精确延迟;
  • 典型场景:下单 30 分钟未支付自动关单、超时重试、定时提醒;
  • 业务细节:延迟到期只是信号,消费时仍要回查订单状态再决定是否关单,防止支付成功与超时并发;
  • 自我治理:死信消费要设最大重试次数与告警,避免补偿逻辑再次失败后消息被无限循环投递。

三、声明示例

Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "ex.order.dlx");      // 死信交换机
args.put("x-dead-letter-routing-key", "key.order.timeout"); // 死信路由键
channel.queueDeclare("q.order", true, false, false, args);
// 消息本身设置 TTL:30 分钟未消费则过期进入死信队列
// TTL 单位为毫秒,30 分钟即 30 * 60 * 1000

四、延迟队列选型速记

  • 对精度不敏感、不想装插件:TTL + DLX 模拟;
  • 需要单条精确延迟、延迟跨度大:x-delayed-message 插件;
  • 需要多级定时精度与事务消息:直接换 RocketMQ 定时消息。

常见追问 / 记忆点

  • 追问:TTL 方案为什么延迟不准?答:RabbitMQ 只检查队头消息是否过期,队头阻塞导致后续消息延迟被放大。
  • 追问:延迟精度要求高怎么办?答:用 x-delayed-message 插件,或改用 RocketMQ 的定时消息。
  • 追问:死信队列消费时要注意什么?答:先记录失败原因再处理,处理仍失败要防止死信死循环,可设定最大重试次数。
  • 记忆点:死信三来源是拒绝、过期、超限;延迟队列要么 TTL+DLX 要么插件,队头阻塞是高频追问点。
笔记加载中…