这是《RocketMQ 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 重试与死信是消费链路的最后防线。重试解决临时故障,死信收集无法自动恢复的消息。设计目标不是无限重试,而是在延迟、吞吐、数据安全和人工介入之间取得平衡。
11.1 消费重试流程
集群模式下,消费失败的消息进入重试队列:
Consumer
-> consume fail
-> RECONSUME_LATER
-> retry topic: %RETRY%consumer-group
-> delay by retry times
-> retry consume
-> final fail
-> dead letter topic: %DLQ%consumer-group
重试次数由消费组配置控制。超过上限后进入死信队列,具体默认值和级别与客户端、Broker 版本相关,应以实际版本配置为准。
11.2 可重试与不可重试
| 异常 | 建议 |
|---|---|
| 数据库连接超时 | 重试 |
| 下游服务 503 | 重试 |
| 网络抖动 | 重试 |
| 参数格式错误 | 记录后确认 |
| 业务状态不允许 | 记录后确认 |
| 权限不足 | 人工处理 |
| 反序列化失败 | 死信治理 |
示例:
try {
handle(msg);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (BusinessRejectException e) {
deadLetterService.save(msg, e);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (TemporaryException e) {
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
} catch (Exception e) {
deadLetterService.save(msg, e);
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
不可修复异常直接进入死信或治理表,可以避免集群被脏消息拖垮。
11.3 重试延迟
重试延迟随次数增长,常见趋势:
10s -> 30s -> 1m -> 2m -> ... -> hours
设计原则:
- 首次重试不要过短,避免故障放大;
- 后续延迟指数增长;
- 设置最大重试次数;
- 核心业务保留人工处理入口;
- 对同一事件的重试做并发控制;
- 监控重试年龄。
11.4 死信队列
死信 Topic 命名通常为:
%DLQ%order-consumer-group
死信不是垃圾箱,而是待处理任务池。治理动作包括:
- 查看原消息、异常和堆栈;
- 修复代码或配置;
- 修复下游数据;
- 校验业务状态;
- 选择重放或放弃;
- 记录处理结论;
- 沉淀告警规则。
建议保存结构化治理记录:
{
"messageId": "msg-id",
"topic": "OrderTopic",
"consumerGroup": "order-consumer-group",
"retryTimes": 16,
"firstFailureAt": "2026-08-25T10:00:00+08:00",
"lastError": "InventoryService 503",
"action": "REPLAY",
"operator": "ops@example.com"
}
11.5 重放策略
重放前必须确认:
- 消费逻辑已修复;
- 消息体可解析;
- 下游容量足够;
- 幂等逻辑有效;
- 不会覆盖更新的业务状态;
- 顺序消息不会破坏时序;
- 批量重放有限速。
安全流程:
导出死信
-> 人工或自动校验
-> 灰度小批量重放
-> 对比业务结果
-> 分批处理剩余
-> 记录处理结论
11.6 幂等与重试
重试天然带来重复执行:
consumer -> deduct inventory success
consumer -> commit offset fail
broker -> redeliver message
处理方式:
@Transactional
public void handle(OrderEvent event) {
if (processedEventRepository.insertIfAbsent(event.eventId()) == 0) {
return;
}
inventoryService.reserve(event.orderNo());
}
同时为业务动作设计唯一键或状态机条件更新,避免“事件表唯一但业务重复执行”。
11.7 监控指标
consumer_retry_total
consumer_retry_delay_seconds
consumer_retry_active
dead_letter_total
dead_letter_oldest_age_seconds
dead_letter_unhandled_count
message_consume_age_seconds
business_reject_total
告警建议:
- 重试率突增;
- 单一错误类型集中出现;
- 死信持续增长;
- 死信未处理时间超过 SLA;
- 重试队列消息年龄过高;
- 消费成功率下降。
11.8 常见问题
| 问题 | 原因 |
|---|---|
| 消息一直重试 | 异常未分类、下游持续失败 |
| 死信快速增加 | 代码缺陷、消息格式变化、权限问题 |
| 重放重复扣款 | 幂等缺失 |
| 找不到重试 Topic | 消费模式、版本或权限问题 |
| 顺序消息卡住 | 有序消费挂起未处理 |
| 死信处理后被覆盖 | 状态机缺少版本 |
本章小结
重试适合处理临时故障,死信负责承接无法自动恢复的消息。消费逻辑必须区分可重试与不可重试异常,设置有限重试和治理流程,并通过幂等、状态机、监控和审计保证重放安全。不要把死信当成终点,它是事件治理的起点。
思考题
- 为什么参数错误不适合无限重试?
- 死信队列和业务治理表如何配合?
- 重放前必须验证哪些条件?
- 重试为什么会放大下游故障?
- 死信未处理时间为什么比死信数量更适合告警?