深度分析 RocketMQ 消息重试(重读)机制:原理、流程、配置与生产实战
一、前言
在分布式系统里,消费失败是常态:
- 网络抖动、调用超时
- 数据库异常、锁等待
- 业务校验失败、流量突刺
- 消费者重启、扩容、宕机
如果没有可靠的消息重试(重读)机制 ,就会出现:数据不一致、丢消息、业务中断、人工补单。
RocketMQ 提供了一套全自动、可配置、隔离性强的消费重试方案。
二、什么是消息重试(重读)?
消息重试 = 消费失败后,Broker 自动重新投递消息给消费者。
RocketMQ 只保证:At Least Once(至少投递一次)不保证 Exactly Once。
所以:消费失败 = 不返回成功 = 自动重试
三、哪些情况会触发重试?
只要消费者没有明确返回成功,就会重试:
- 业务代码抛出 Exception / Error
- 显式返回
RECONSUME_LATER - 消费超时(默认 15min)
- 消费者进程挂掉、断开连接
- 队列重新负载均衡(rebalance)
只要没返回:
java
ConsumeConcurrentlyStatus.CONSUME_SUCCESS
一律进入重试。
四、核心机制:重试消息存在哪里?
重点:重试消息不在原 Topic!
RocketMQ 会为每个消费组 创建一个内置重试队列:
shell
%RETRY%+消费者组名
例如:消费组:order-consumer-group重试队列:%RETRY%order-consumer-group
特点:
- 自动创建,无需手动建
- 对业务透明
- 与正常 Topic 隔离,不影响主流程
- 只有当前消费组能消费
五、重试延迟规则(非常重要)
RocketMQ 不是立即重试 ,而是使用延迟等级控制间隔。
默认 18 个延迟级别:
1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
对应 level:1 ~ 18
重试次数与延迟对应
- 第 1 次重试 → level 3 → 10s
- 第 2 次重试 → level 4 → 30s
- 第 3 次重试 → level 5 → 1m
- 第 4 次重试 → level 6 → 2m
- ...
- 越来越长,避免雪崩式重试
六、最大重试次数 & 死信队列(DLQ)
1. 默认最大重试次数
- 集群模式:默认 16 次
- 广播模式:不重试(只发一次)
2. 超过最大重试 → 进入死信队列
死信队列格式:
shell
%DLQ%+消费者组名
特点:
- 进入死信后 不再自动投递
- 需要人工排查、处理、恢复
- 是分布式系统最后的兜底保障
七、集群模式 vs 广播模式 重试对比
表格
| 模式 | 是否重试 | 重试队列 | 死信队列 | 适用场景 |
|---|---|---|---|---|
| 集群模式 | ✅ 是 | ✅ 有 | ✅ 有 | 生产环境默认 |
| 广播模式 | ❌ 不重试 | ❌ 无 | ❌ 无 | 日志、通知 |
生产 99% 用集群模式。
八、完整重试流程(图文版逻辑)
erlang
消费开始
↓
执行业务逻辑
↓
成功?→ 返回 CONSUME_SUCCESS → 结束
↓
失败?
↓
写入 %RETRY%消费组 队列
↓
按延迟等级等待
↓
重新投递给消费者
↓
重复直到达到最大次数
↓
进入 %DLQ%消费组 死信队列
↓
人工处理
九、代码层面如何控制重试?
1. 抛异常自动重试(最常用)
java
@Override
public ConsumeConcurrentlyStatus consumeMessage(
List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
try {
// 业务处理
doBusiness(msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (Exception e) {
log.error("消费异常,自动重试", e);
// 抛出异常 → 自动进入重试
throw e;
}
}
2. 手动返回重试
java
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
3. 不想重试?直接吞异常
java
try {
doBusiness();
} catch (Exception e) {
log.error("业务失败,不再重试", e);
}
return CONSUME_SUCCESS;
⚠️ 谨慎使用,会丢消息。
十、如何自定义最大重试次数?
java
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("order-group");
// 设置最大重试次数
consumer.setMaxReconsumeTimes(5);
推荐:3~5 次,避免无限重试。
十一、生产环境最佳实践
-
消费必须幂等重试一定会带来重复消息,必须保证重复消费不影响结果。
-
可重试与不可重试异常分开处理
- 网络 / DB 抖动:可重试
- 参数非法 / 数据不存在:不重试,直接记录
-
快速失败,不要阻塞阻塞会导致队列大量堆积。
-
死信队列必须监控告警出现死信 = 系统异常,必须及时处理。
-
重试次数不要设置过大避免消息长期占用队列,影响正常消费。
-
禁止禁用重试高可靠系统不允许丢消息。
十二、总结
RocketMQ 消息重试(重读)机制是:高可靠分布式系统的核心保障。
核心要点:
- 消费失败 → 进入
%RETRY%组名 - 按延迟等级重试,避免风暴
- 超过次数 → 进入
%DLQ%组名 - 消费端必须做幂等
- 生产必须监控重试、死信、堆积
理解这套机制,你就能真正写出生产可用、高可靠、高可用的消息服务。