← 返回题目列表

什么是死信队列和延迟消息?

高频 中等 第 2 / 25 题 更新于 2026/07/28
消息队列死信队列延迟消息重试

简化版

死信队列(DLQ, Dead Letter Queue) 是专门存放「处理失败、无法被正常消费的消息」的特殊队列——一条消息重试多次仍失败、或过期/被拒绝后,就被投递到死信队列,避免它一直阻塞正常消费,同时保留下来供人工排查或后续处理。延迟消息(Delay Message) 是指消息发送后不立即投递,而是延迟指定时间后才对消费者可见——用于「下单后 30 分钟未支付自动取消」「延迟通知」等定时场景。RocketMQ 原生支持延迟消息(固定延迟级别),RabbitMQ 靠 TTL + 死信或插件实现。

详细版

死信队列——消息进入死信的常见条件:

  • 消息被消费者多次重试仍失败(超过最大重试次数)。
  • 消息被消费者拒绝(reject/nack)且不重新入队。
  • 消息在队列中过期(超过 TTL)未被消费。
  • 队列达到最大长度,新消息进不来(RabbitMQ)。

延迟消息——典型应用:

  • 订单超时未支付,30 分钟后自动关闭。
  • 延迟发送提醒 / 通知。
  • 定时任务的替代(到点触发)。

各 MQ 的延迟消息实现:

  • RocketMQ:原生支持,预设 18 个延迟级别(1s、5s、10s…1h、2h),发消息时设 delayLevel。
  • RabbitMQ:用 TTL(消息过期)+ 死信队列 组合,或用 rabbitmq-delayed-message-exchange 插件。
  • Kafka:无原生支持,需自己实现(如时间轮 + 定时 Topic 转发)。

完整版教学

一、死信队列:给”处理不了的消息”一个归宿

正常情况下,消息被消费者成功处理后就完成使命。但总有些消息怎么都处理不成功——比如消息格式错误、业务数据异常、依赖的下游一直不可用。如果对这种消息无限重试,会:

  • 阻塞正常消费(尤其顺序消费,一条卡住后面全堵);
  • 浪费资源(一直重试消耗 CPU、下游压力);
  • 刷屏日志、掩盖其他问题。

死信队列就是给这类消息一个专门的「归宿」:当一条消息满足「死信条件」(重试超限、被拒绝、过期等),MQ 就把它从原队列移到死信队列。好处:

  • 不再阻塞正常队列,正常消息继续流转。
  • 消息不丢,被保留在死信队列里。
  • 可人工介入:运维/开发去死信队列查看这些失败消息,分析原因、修复后重新投递或手动处理。

二、消息进入死信的触发条件

不同 MQ 略有差异,但常见触发条件:

  • 重试次数耗尽:消费者处理失败会重试,超过最大重试次数(如 RocketMQ 默认 16 次)后,消息进死信队列。
  • 消息被拒绝:消费者显式 reject/nack 且不要求重新入队(RabbitMQ requeue=false)。
  • 消息过期:消息设置了 TTL,在队列里超时未被消费(RabbitMQ)。
  • 队列满了:队列达到最大长度限制,新消息无处可放(RabbitMQ)。

三、死信队列的处理策略

消息进了死信队列后怎么办?取决于业务:

  • 告警 + 人工处理:监控死信队列,有消息就告警,人工排查原因、决定重投或丢弃。
  • 自动重投:修复问题后(如下游恢复),把死信消息重新投回原队列消费。
  • 记录归档:把死信消息落库存档,用于对账、审计、问题分析。

死信队列的核心价值是**「隔离 + 保留 + 可追溯」**——把毒消息隔离出来、保留下来、便于事后处理,而不是让它们在正常链路里捣乱或悄悄丢失。

四、延迟消息:让消息”到点再送达”

延迟消息指消息发送后不立即投递给消费者,而是等待指定的延迟时间后才变得可消费。最经典的应用是订单超时关闭

用户下单时,发一条「延迟 30 分钟」的消息。30 分钟后这条消息才被消费者收到,消费者检查订单状态——如果还是「未支付」就自动关闭订单、释放库存。

用延迟消息实现这类「定时触发」逻辑,比用定时任务轮询数据库(每分钟扫一遍所有未支付订单)高效得多——不用轮询、精准到点、天然分布式。

其他应用:延迟发送通知/提醒、定时开启某活动、重试的退避延迟(第 1 次失败 1 分钟后重试、第 2 次 5 分钟后…)。

五、各 MQ 如何实现延迟消息

  • RocketMQ(原生支持):内置固定的延迟级别(默认 18 个:1s、5s、10s、30s、1m、2m…1h、2h)。发消息时设置 delayLevel(如 level=3 表示延迟 10s)。简单好用,但只能选预设级别,不能任意指定时间(RocketMQ 5.x 支持任意时间的定时消息)。
  • RabbitMQ(组合实现):经典做法是 TTL + 死信队列——把消息发到一个设置了 TTL、且无消费者的「延迟队列」,消息 TTL 到期后变成死信,被转发到真正的业务队列消费,从而实现延迟。或使用官方 rabbitmq-delayed-message-exchange 插件,支持任意延迟时间。
  • Kafka(无原生支持):需要自己实现,常见方案是时间轮(TimingWheel)+ 分级 Topic——按延迟时长分成不同 Topic,用定时任务扫描到期消息再转发到目标 Topic。较复杂。

六、常见误区与追问

考点正确口径
死信队列无法正常消费的消息进入专门队列
延迟消息消息到达后延迟一段时间再投递
用途失败隔离、人工补偿、超时关闭订单、定时触发
consume failed 16 times
or message expired
or queue rejected
-> DLQ

order created -> delay 30min -> check unpaid -> close order

死信用于“失败隔离和补偿”,延迟消息用于“未来某个时间再处理”。

  • 误区:死信队列就是丢弃消息。 死信是把异常消息转移到专门队列,便于排查、告警和补偿。
  • 误区:延迟消息一定精确到毫秒。 不同 MQ 的延迟精度和实现不同,通常不能当强实时定时器。
  • 误区:进了死信就不用管。 死信需要监控、报警、人工或自动补偿,否则只是把问题藏起来。
  • 追问:哪些消息会进死信? 消费重试耗尽、消息过期、队列满或被拒绝等都可能进入死信。
  • 追问:延迟队列如何实现? 可用 MQ 原生延迟、定时轮询、时间轮、死信 TTL 转发等方式。
  • 追问:订单超时关闭为什么适合延迟消息? 创建订单时发延迟消息,到期检查未支付再关闭,避免频繁扫描全表。

七、加强记忆

死信队列(DLQ) = 存放处理失败、无法正常消费的消息的特殊队列,触发条件有重试超限、被拒绝、过期、队列满。价值是隔离毒消息(不阻塞正常队列)+ 保留不丢 + 可人工追溯重投延迟消息 = 发送后延迟指定时间才可被消费,经典用于订单超时自动关闭(比轮询 DB 高效)、延迟通知、退避重试。实现:RocketMQ 原生支持(预设延迟级别)RabbitMQ 用 TTL + 死信队列组合或延迟插件Kafka 无原生、需时间轮 + 分级 Topic 自实现。核心区分:死信是「处理不了的消息的归宿」,延迟是「到点才送达的消息」