深度分析RocketMQ 消息重试(重读)机制

深度分析 RocketMQ 消息重试(重读)机制:原理、流程、配置与生产实战

一、前言

在分布式系统里,消费失败是常态

  • 网络抖动、调用超时
  • 数据库异常、锁等待
  • 业务校验失败、流量突刺
  • 消费者重启、扩容、宕机

如果没有可靠的消息重试(重读)机制 ,就会出现:数据不一致、丢消息、业务中断、人工补单

RocketMQ 提供了一套全自动、可配置、隔离性强的消费重试方案。

二、什么是消息重试(重读)?

消息重试 = 消费失败后,Broker 自动重新投递消息给消费者。

RocketMQ 只保证:At Least Once(至少投递一次)不保证 Exactly Once。

所以:消费失败 = 不返回成功 = 自动重试


三、哪些情况会触发重试?

只要消费者没有明确返回成功,就会重试:

  1. 业务代码抛出 Exception / Error
  2. 显式返回 RECONSUME_LATER
  3. 消费超时(默认 15min)
  4. 消费者进程挂掉、断开连接
  5. 队列重新负载均衡(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 次,避免无限重试。


十一、生产环境最佳实践

  1. 消费必须幂等重试一定会带来重复消息,必须保证重复消费不影响结果。

  2. 可重试与不可重试异常分开处理

    • 网络 / DB 抖动:可重试
    • 参数非法 / 数据不存在:不重试,直接记录
  3. 快速失败,不要阻塞阻塞会导致队列大量堆积。

  4. 死信队列必须监控告警出现死信 = 系统异常,必须及时处理。

  5. 重试次数不要设置过大避免消息长期占用队列,影响正常消费。

  6. 禁止禁用重试高可靠系统不允许丢消息。


十二、总结

RocketMQ 消息重试(重读)机制是:高可靠分布式系统的核心保障

核心要点:

  • 消费失败 → 进入 %RETRY%组名
  • 按延迟等级重试,避免风暴
  • 超过次数 → 进入 %DLQ%组名
  • 消费端必须做幂等
  • 生产必须监控重试、死信、堆积

理解这套机制,你就能真正写出生产可用、高可靠、高可用的消息服务。

相关推荐
愿你天黑有灯下雨有伞2 天前
高效异步处理:基于RocketMQ的延迟消费系统架构全解析
系统架构·rocketmq
堕落年代3 天前
RocketMQ 5.x + Spring Boot 发送消息失败全解析
spring boot·rocketmq·java-rocketmq
三水不滴3 天前
深度分析 RocketMQ 幂等性设计与生产级实现方案
后端·rocketmq·设计
三水不滴3 天前
深度分析:RocketMQ 如何保证消息不丢失 —— 全链路防丢机制与生产实践
rocketmq
瑶山3 天前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
洛豳枭薰5 天前
消息队列关键问题描述
kafka·rabbitmq·rocketmq
wsx_iot5 天前
RocketMQ使用
rocketmq
奋斗羊羊7 天前
rocketmq 及依赖环境编译安装过程记录(windows)
windows·rocketmq
没有bug.的程序员7 天前
RocketMQ 与 Kafka 深度对垒:分布式消息引擎内核、事务金融级实战与高可用演进指南
java·分布式·kafka·rocketmq·分布式消息·引擎内核·事务金融