Kafka的死信队列

Kafka本身没有内置死信队列 ,死信队列是业务层面的设计方案:消费失败、无法正常处理的消息,转发到专门的死信 Topic,不再重复重试消费,避免阻塞消费。

一、什么时候消息进死信

常见场景:

  1. 业务异常:消息格式错误、数据非法、缺少字段,无论重试多少次都处理失败(不可恢复错误
  2. 重试次数耗尽:重试 N 次后仍然失败,不再继续重试
  3. 超时、消息内容损坏、反序列化失败

区分:临时故障(数据库短暂不可用)→ 重试;数据本身坏了 → DLQ

二、核心架构(经典链路)

复制代码
原始Topic → Consumer消费
    ↓
消费失败
    ├─可重试异常 → 发送到【重试Topic】,延迟一段时间后重新消费
    └─不可重试异常 / 重试次数用完 → 发送到【死信Topic(DLQ)】
  • 原始 Topic:正常业务消息
  • 重试 Topic:用来做延迟重试(可以多个,按重试轮次拆分)
  • DLQ Topic:存放彻底失败消息,不再自动消费,人工排查

Kafka 没有延迟消息,重试一般两种实现:

  1. 单重试 Topic + 消费时 sleep(简单,不推荐高吞吐)
  2. 多个重试 topic,不同延时,定时轮询(主流方案)

三、实现思路(Consumer 侧)

  1. 消费消息,执行业务逻辑
  2. try-catch 捕获异常
    • 如果是可重试异常:记录重试次数,发到重试 Topic
    • 如果是不可重试异常,或者重试次数 ≥ 阈值:发送到 DLQ
  3. 提交 offset(重点! ) ✅ 必须提交 offset,否则这条消息会无限重放,阻塞整个消费分区 很多新手坑:不提交 offset,消息一直卡在原始队列反复抛错

伪代码示意

复制代码
while (true) {
    ConsumerRecords records = consumer.poll();
    for (ConsumerRecord record : records) {
        try {
            process(record); //业务处理
        } catch (BusinessException e) {
            // 不可恢复异常,转入DLQ
            sendToDLQ(record);
        } catch (DBTempException e) {
            //临时异常,重试
            int retryCount = getRetryCount(record);
            if(retryCount < 3){
                sendToRetryTopic(record, retryCount+1);
            }else{
                sendToDLQ(record);
            }
        }
        consumer.commitSync(); //提交offset,原始消息不再拉取
    }
}

四、DLQ Topic 设计要点

  1. 分区数:建议和原 topic 分区数保持一致,或者根据死信量单独评估
  2. 消息保留时间:设置更长保留时长(比如 7 天),方便排查,到期自动清理
  3. 消息附加元数据(非常重要) 转发到 DLQ 时,不要只转发原始消息体 ,额外带上 header:
    • 原 topic、分区、offset
    • 失败时间、异常堆栈
    • 重试次数
    • 消息产生时间 方便定位是哪条消息、什么时候失败
  4. 消费隔离:死信 Topic 单独消费,不能和业务 consumer 共用 死信一般人工消费、导出、审计、修复后重放,不自动回写业务。

五、常见坑

  1. ❌ 忘记提交 offset → 消息无限重试,消费卡死
  2. ❌ 死信不带元数据 → 出问题找不到原始上下文
  3. ❌ 所有异常一律丢 DLQ:临时故障也直接丢,消息丢失
  4. ❌ DLQ 不做监控:死信堆积没人发现,故障延迟很久才感知

建议监控:DLQ 消息数量,一旦新增消息触发告警

六、框架内置支持

很多框架封装好了 DLQ,不用自己写转发逻辑:

  • Spring Kafka:DeadLetterPublishingRecoverer,配置重试、死信
  • Flink Kafka Consumer、Spark Streaming 也提供 DLQ 配置
  • 原生 Kafka 客户端:需要自己编码实现转发

七、死信消息后续怎么处理

  1. 监控告警:DLQ 有消息触发告警
  2. 排查:读取死信消息 + 异常信息定位 bug
  3. 修复数据 / 代码后:
    • 方案 1:写工具消费 DLQ,修复后重新投递到原始 topic
    • 方案 2:人工补发
相关推荐
想要打 Acm 的小周同学呀20 小时前
Kafka消息队列出现挤压如何解决?
分布式·kafka
富士康质检员张全蛋1 天前
Kafka实战 自定义分区器 几种常见的分区器
分布式·kafka
starzy19901 天前
Flink基础之Flink批流数据读取处理案例剖析:从文件到 Kafka 的完整代码
大数据·flink·kafka
数智启示录2 天前
Apache Kafka Consumer 扩到 40 个仍不提速:Partition 上限锁死有效并行度 【Kafka合集】
数据库·经验分享·分布式·缓存·面试·kafka·apache
数智启示录2 天前
Apache Kafka 幂等 Producer 的边界:Exactly-Once 到了 MySQL 为什么失效 【Kafka合集】
数据库·经验分享·分布式·mysql·面试·kafka·apache
数智启示录2 天前
Apache Kafka 不只是消息队列:日志与 Offset 分离如何让事件可重放 【Kafka合集】
经验分享·分布式·面试·kafka·apache
数智启示录2 天前
Apache Kafka 同 Key 仍会乱序:从分区 Offset 到业务可见的四道边界 【Kafka 合集】
大数据·数据库·经验分享·分布式·面试·kafka·apache
老周聊架构2 天前
Flink 连接器与生态:Kafka Offset 提交修复与 MySQL Binlog 延迟指标
mysql·flink·kafka
小白羊丨3 天前
Kafka任务重试、退避与失败恢复怎么做?
分布式·kafka