中 基础
死信队列#
一句话答案#
消费失败重试超上限后进入死信队列(DLQ),人工介入处理:修复后重新投递或补偿处理。
核心要点
消费失败的处理策略:
1. 重试机制
第一次失败 → 等待 5s 重试
第二次失败 → 等待 30s 重试
第三次失败 → 等待 1min 重试
...
达到最大重试次数 → 投递到死信队列
(指数退避策略,避免频繁重试打爆下游)plaintext2. 死信队列(Dead Letter Queue,DLQ)
死信队列是专门存放”处理失败”消息的队列,消息进入死信队列的条件:
① 消息消费失败且超过最大重试次数
② 消息在队列中等待超过 TTL(消息过期)
③ 队列长度超过上限(消息被挤出)plaintextRocketMQ 的死信队列:
// 消费失败达到最大重试次数(默认16次)后,自动投入死信队列
// 死信 Topic 名称:%DLQ%消费者GroupID
// 监控和处理死信:
consumer.subscribe("%DLQ%MyConsumerGroup", "*");
consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
// 告警、人工处理、记录到 DB 等
alertService.sendAlert(msgs.get(0));
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});javaKafka 的死信队列(需自己实现):
// Kafka 没有原生死信队列,需要自己实现:
public void consume(ConsumerRecord<String, String> record) {
int retryCount = getRetryCount(record);
try {
process(record);
} catch (Exception e) {
if (retryCount < MAX_RETRY) {
// 发到重试 Topic(延迟一定时间)
producer.send(new ProducerRecord<>("topic-retry-" + retryCount, record.value()));
} else {
// 达到最大重试,发到死信 Topic
producer.send(new ProducerRecord<>("topic-dead-letter", record.value()));
// 告警
}
}
}java处理死信的策略:
- 人工介入:告警 → 运营人员在控制台查看死信 → 手动重新处理
- 自动重试:定时任务将死信重新投递(配合幂等性)
- 降级处理:记录到数据库,由补偿任务处理
面试回答(2分钟版)
死信队列是专门存放消费失败消息的队列,相当于消息系统的”退件仓库”。消息进入死信队列有三种条件:消费重试超过最大次数、消息在队列中超过TTL过期、队列长度超过上限被挤出。以RocketMQ为例,消费失败后会按指数退避策略自动重试,从5秒逐步递增到2小时,默认重试16次后消息自动投入死信Topic,Topic名称格式是%DLQ%加ConsumerGroupID。Kafka没有原生死信队列,需要自己实现:消费失败后根据重试次数决定投递到重试Topic还是死信Topic。处理死信的策略一般有三种:人工介入通过监控告警让运维人员在控制台查看并手动处理;自动重试由定时任务将死信重新投递配合幂等性保证;降级处理记录到数据库由补偿任务异步处理。实际生产中必须对死信队列做监控告警,因为死信积压往往意味着下游系统出了严重问题,比如数据格式变更、依赖服务不可用等,需要及时排查根因而不能仅仅依赖重试。
追问与易错
追问方向:
- “死信队列和重试队列的区别?”→ 重试队列用于消费失败后延迟重试(通常有最大重试次数),死信队列是重试耗尽后的最终归宿,存放无法正常消费的消息等待人工介入
- “死信消息怎么处理?”→ 通常设置告警通知开发人员,人工排查失败原因后修复逻辑并重新投递;也可以写专门的死信消费者做补偿处理或记录到数据库供后续分析
- “Kafka 原生支持死信队列吗?”→ Kafka 原生不支持,需要自行实现:消费失败超过重试次数后将消息发送到一个专门的 DLQ Topic;RocketMQ 和 RabbitMQ 原生支持死信队列机制
易错点:
- ❌ 只知道概念不知道原理——面试官会追问底层实现
- ❌ 缺乏实际使用经验——结合项目场景回答更有说服力