RocketMQ 运维排障:堆积、重试与死信¶
30 秒版(开场)¶
RocketMQ 线上问题集中在 消费堆积、重复消费、消息进死信、Broker 磁盘满。排查路径:Console 看 diff(堆积量)→ 消费 TPS vs 生产 TPS → 慢消费/失败重试 → DLQ。生产关键词:maxReconsumeTimes、%DLQ% 主题、CommitLog 磁盘、resetOffset 慎用。
3 分钟版(精讲深度)¶
- 是什么:消费失败按 重试级别 延迟重投(非立即);超过
maxReconsumeTimes进入 死信队列%DLQ%{consumerGroup};堆积 = 生产 offset − 消费 offset。 - 为什么:下游 DB 慢、Bug 抛异常、Consumer 实例不足、Rebalance 导致短暂停消费都会表现为堆积。
- 怎么做:先扩 Consumer / 修 Bug;调
consumeTimeout、maxReconsumeTimes;DLQ 人工回放或修复后 re-send;Broker 监控 磁盘使用率、putMessage 耗时;禁止随意resetOffsetByTime。
10 分钟版(原理 + 流程)¶
flowchart TB
P[Producer] --> T[Topic Queue]
T --> C[Consumer Group]
C -->|处理失败| R[Retry Topic %RETRY%]
R --> C
C -->|超过次数| DLQ[%DLQ% Group]
| 现象 | 常见根因 | 处理 |
|---|---|---|
| 堆积持续涨 | 消费慢于生产 | 加实例、优化逻辑、限流生产 |
| 堆积突刺 | 发布/扩容 Rebalance | 等稳定;cooperative 思想靠少动实例 |
| 反复重试 | 下游超时、脏数据 | 修复 + 跳过 poison(进 DLQ) |
| DLQ 暴涨 | maxReconsumeTimes 过小或逻辑全失败 | 查消费日志、单条回放 |
| Broker 磁盘告警 | CommitLog/ConsumeQueue 未过期 | 调 retention、扩容磁盘、清理 |
| send timeout | Broker 忙、同步刷盘慢 | 异步刷盘权衡、分集群 |
重试机制
- 消费失败 → 消息发回 Retry Topic(
%RETRY%{group}),延迟级别递增(10s、30s、1m…) - 默认
maxReconsumeTimes=16(版本略有差异),耗尽 → DLQ - 顺序消息失败会 阻塞同 Queue 后续消息 → 堆积更明显
死信队列 DLQ
- 命名:
%DLQ%{consumerGroup} - 需 单独订阅 DLQ 做告警、人工修复、补偿脚本
- 不要长期堆积无人处理 → 业务永久丢失
生产场景¶
- 风控规则消费:单条 poison 卡死 → 捕获后写异常表 + return Success,避免无限重试
- 大促订单堆积:临时扩 Consumer;生产端限流;事后
mqadmin consumerProgress核对 - 事务消息半消息堆积:查
RMQ_SYS_TRANS_HALF_TOPIC+ 回查失败(见 S-RMQ-02)
排查与工具¶
| 工具 | 命令/能力 |
|---|---|
| RocketMQ Console | Topic 堆积、Consumer 在线、TPS |
mqadmin clusterList |
Broker 存活 |
mqadmin consumerProgress -g {group} |
diff、lastOffset |
mqadmin queryMsgById |
按 msgId 查轨迹 |
mqadmin resetOffsetByTime |
慎用:按时间重置进度 |
| Broker 日志 | store error、flush timeout |
排查路径
- Console:哪个 Group、哪个 Queue diff 最大
- 该 Group 消费 TPS 是否 ≈ 0(全挂 vs 慢)
- 应用日志:异常栈、DB 超时
- 是否 DLQ / Retry Topic 也在涨
- Broker 磁盘、CPU、PageCache
架构取舍¶
| 手段 | 适用 | 风险 |
|---|---|---|
| 扩 Consumer | 并行度未打满 Queue | 超过 Queue 数无效 |
| 提高并发消费线程 | IO 型消费 | 乱序、DB 连接池打满 |
| 调大 maxReconsumeTimes | 临时抖动 | poison 延迟进 DLQ |
| resetOffset | 补数、测试 | 生产误操作重复/丢消费 |
| 跳过失败消息 | 非关键链路 | 数据不一致 |
深挖问答¶
- 和 Kafka lag 区别? → 概念类似 diff/lag;RocketMQ 还有 Retry/DLQ 独立 Topic(见 S-DIST-04)。
- 重复消费怎么防? → 业务幂等键;Rebalance 也会重复(S-ARCH-04)。
- 顺序消息堆积? → 单 Queue 单线程消费,不能简单加实例;需拆分 ShardingKey 或优化单条耗时。
- Go 优雅下线? → SIGTERM →
Shutdownconsumer → 处理 in-flight → 再退出 Pod(S-CODE-03)。 - Broker 主从切换? → 同步复制下少量未同步消息可能丢;监控 slave fall behind。
反模式与事故¶
- 生产直接
resetOffset到最新 → 大量消息未消费即丢弃 - DLQ 无监控、无回放流程 → 静默丢单
- 消费逻辑抛 panic 未捕获 → 无限重试打满 Broker
- 只扩 Producer 不扩 Consumer → 堆积至磁盘满,全集群不可用
- 顺序消费开多线程 → 乱序、账务错误
代码示例¶
// 消费失败:返回 RetryLater 会进入重试队列(rocketmq-client-go v2 示意)
type Handler struct{}
func (h *Handler) Consume(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for _, m := range msgs {
if err := process(m); err != nil {
if isPoison(err) {
// 只有在异常记录/补偿任务已可靠持久化后才能返回 Success;
// 只打印日志再 Success 会永久跳过这条业务消息。
if savePoisonForRepair(ctx, m, err) != nil {
return consumer.ConsumeRetryLater, nil
}
return consumer.ConsumeSuccess, nil
}
return consumer.ConsumeRetryLater, nil
}
}
return consumer.ConsumeSuccess, nil
}