面试知识库
中 基础

死信队列#

一句话答案#

消费失败重试超上限后进入死信队列(DLQ),人工介入处理:修复后重新投递或补偿处理。

核心要点

消费失败的处理策略:

1. 重试机制

第一次失败 → 等待 5s 重试
第二次失败 → 等待 30s 重试
第三次失败 → 等待 1min 重试
...
达到最大重试次数 → 投递到死信队列

(指数退避策略,避免频繁重试打爆下游)
plaintext

2. 死信队列(Dead Letter Queue,DLQ)

死信队列是专门存放”处理失败”消息的队列,消息进入死信队列的条件:

① 消息消费失败且超过最大重试次数
② 消息在队列中等待超过 TTL(消息过期)
③ 队列长度超过上限(消息被挤出)
plaintext

注意:②③ 是 RabbitMQ 的死信条件(RabbitMQ 官方列了 4 种:basic.reject/basic.nack 且 requeue=false、消息 TTL 过期、超出队列长度上限、Quorum Queue 重投次数超过 delivery-limit)。RocketMQ 进死信只有「重试次数耗尽」这一种。

RocketMQ 的死信队列:

// 消费失败达到最大重试次数(默认16次)后,自动投入死信队列
// 死信 Topic 名称:%DLQ%消费者GroupID(按消费组划分,不按原 Topic)
// 重试间隔按 10s 30s 1m 2m ... 2h 阶梯递增(复用延迟级别 3~18)
// 注意:早期 4.x 版本自动创建的 DLQ Topic 权限是 perm=2(只写),要先改成 6 才能订阅;当前源码已按读写权限创建

// 监控和处理死信:
consumer.subscribe("%DLQ%MyConsumerGroup", "*");
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
    // 告警、人工处理、记录到 DB 等
    alertService.sendAlert(msgs.get(0));
    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});
java

Kafka 的死信队列(Broker 不内置,需自己实现或借助框架):

Spring Kafka 有 DeadLetterPublishingRecoverer,Kafka Connect 有 errors.deadletterqueue.topic.name,本质都是客户端把失败消息转发到一个 DLQ Topic。Kafka 4.2 起生产可用的 Share Group(KIP-932 Queues for Kafka)有逐条 ack 和投递次数计数,超过次数的记录会被归档丢弃,仍不是 RabbitMQ 那种自动转存的死信队列。

处理死信的策略:

  1. 人工介入:告警 → 运营人员在控制台查看死信 → 手动重新处理
  2. 自动重试:定时任务将死信重新投递(配合幂等性)
  3. 降级处理:记录到数据库,由补偿任务处理

面试回答(2分钟版)

死信队列是专门存放消费失败消息的队列,相当于消息系统的”退件仓库”。消息进入死信队列有三种条件:消费重试超过最大次数、消息在队列中超过TTL过期、队列长度超过上限被挤出。以RocketMQ为例,消费失败后会按阶梯递增的间隔自动重试,从10秒逐步递增到2小时,默认重试16次后消息自动投入死信Topic,Topic名称格式是%DLQ%加ConsumerGroupID。Kafka没有原生死信队列,需要自己实现:消费失败后根据重试次数决定投递到重试Topic还是死信Topic。处理死信的策略一般有三种:人工介入通过监控告警让运维人员在控制台查看并手动处理;自动重试由定时任务将死信重新投递配合幂等性保证;降级处理记录到数据库由补偿任务异步处理。实际生产中必须对死信队列做监控告警,因为死信积压往往意味着下游系统出了严重问题,比如数据格式变更、依赖服务不可用等,需要及时排查根因而不能仅仅依赖重试。

追问与易错

追问方向:

  • “死信队列和重试队列的区别?”→ 重试队列用于消费失败后延迟重试(通常有最大重试次数),死信队列是重试耗尽后的最终归宿,存放无法正常消费的消息等待人工介入
  • “死信消息怎么处理?”→ 通常设置告警通知开发人员,人工排查失败原因后修复逻辑并重新投递;也可以写专门的死信消费者做补偿处理或记录到数据库供后续分析
  • “Kafka 原生支持死信队列吗?”→ Kafka 原生不支持,需要自行实现:消费失败超过重试次数后将消息发送到一个专门的 DLQ Topic;RocketMQ 和 RabbitMQ 原生支持死信队列机制

易错点:

  • ❌ 消息进了死信队列就算处理完了——死信只是隔离,不告警、不排查、不重投,等于静默丢消息;DLQ 要配监控告警和重投工具
  • ❌ 修完 bug 把死信原样全部重投——先确认消费逻辑幂等,再判断消息是否已过时(如订单已取消);顺序敏感的业务重投还会打乱顺序
  • ❌ RocketMQ 死信 Topic 按原 Topic 建——实际按消费组建(%DLQ% + 消费组名),同一个 Topic 被多个组消费时,每个组有自己的死信 Topic