深度分析:RocketMQ 如何保证消息不丢失 ------ 全链路防丢机制与生产实践
一、前言
在分布式系统中,消息可靠性是核心指标。很多人以为用了 MQ 就天然不丢消息,其实完全错了。
RocketMQ 本身不保证 100% 绝对不丢 ,但通过正确的架构 + 正确的配置 + 正确的使用姿势 ,可以达到金融级、生产级不丢失。
消息丢失只会发生在三个阶段:
- Producer → Broker(发送丢失)
- Broker 内存 → 磁盘(存储丢失)
- Broker → Consumer(消费丢失)
本文把这三部分全部讲透。
二、第一阶段:生产者如何保证消息不丢
生产者丢消息,一般是:消息发了,但 Broker 没收到、没返回 ACK、生产者直接忽略异常。
2.1 使用同步发送(最关键)
- 同步发送
send():等待 Broker 返回 ACK,失败直接抛异常。 - 严禁使用
sendOneway():发完不管,必丢消息。
java
// 同步发送(推荐)
SendResult result = producer.send(message);
2.2 开启生产者重试
java
// 同步发送失败重试次数
producer.setRetryTimesWhenSendFailed(3);
// 异步发送失败重试次数
producer.setRetryTimesWhenSendAsyncFailed(3);
2.3 严格校验发送结果
必须判断 SendStatus,失败一定要重试 / 告警 / 落库。
java
if (result.getSendStatus() != SendStatus.SEND_OK) {
// 日志、告警、重试、持久化待重发
}
2.4 极高可靠场景:使用事务消息
订单 / 支付等核心业务,直接用 事务消息 ,从根源保证:本地事务成功 ↔ 消息一定发送成功
三、第二阶段:Broker 如何保证消息不丢(最核心)
Broker 是最容易丢消息的环节,99% 是因为:消息还在 PageCache,机器断电 / 宕机,数据丢失。
3.1 刷盘策略:同步刷盘
RocketMQ 默认是 异步刷盘,性能高,但可能丢消息。
要绝对不丢,必须开启:
properties
flushDiskType = SYNC_FLUSH
- 消息真正写入磁盘才返回 ACK
- 机器宕机、断电都不会丢
- 金融 / 支付必开
3.2 主从同步:同步双写
单机一定有风险,必须部署主从。
properties
brokerRole = SYNC_MASTER
- 必须 主 + 从都写入成功 才返回成功
- 主机挂了,从机依然有完整数据
3.3 最强不丢配置(金融级)
properties
flushDiskType=SYNC_FLUSH
brokerRole=SYNC_MASTER
这套配置,只要磁盘不坏,绝对不丢。
四、第三阶段:消费者如何保证消息不丢(最容易忽略)
消费端丢消息,是最经典的坑:
业务还没执行完,就自动 ACK,服务一挂,消息永久丢失。
4.1 核心原则:先执行业务,再 ACK
RocketMQ 消费者逻辑:
- 返回 CONSUME_SUCCESS → 认为消息消费完成
- 抛出异常 / 返回 RECONSUME_LATER → 会重试
正确写法:
java
@Override
public ConsumeConcurrentlyStatus consumeMessage(...) {
try {
// 1. 先执行业务(入库、调用、处理)
doBusiness();
// 2. 成功后再返回 SUCCESS
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
} catch (Exception e) {
log.error("消费失败", e);
// 3. 异常 → 不 ACK,Broker 会重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
4.2 严禁提前 ACK
不要在业务执行前就返回成功。
4.3 消费超时合理设置
避免因为超时导致重复或丢失。
五、消息不丢失完整流程(总结一张图看懂)
生产者
↓(同步发送 + 校验结果 + 重试)
Broker
↓(同步刷盘 + 同步主从)
消费者
↓(先执行业务,成功再 ACK)
最终:消息不丢失
六、生产环境终极最佳实践(直接照做)
生产者
- 使用同步发送
- 开启重试
- 校验 SendResult
- 核心业务用事务消息
- 失败消息落库 + 定时任务重试
Broker
ini
flushDiskType=SYNC_FLUSH
brokerRole=SYNC_MASTER
- 主从架构
- 磁盘 RAID
- 监控磁盘健康、内存、服务状态
消费者
- 先处理业务,再 ACK
- 异常返回 RECONSUME_LATER
- 必须做幂等(重试会重复)
- 监控:堆积、重试、死信
七、经典问题
- RocketMQ 如何保证消息不丢失?
- 同步刷盘 vs 异步刷盘 区别?
- 同步主从 vs 异步主从 区别?
- 消费端为什么会丢消息?
- 事务消息为什么能防丢?
- 生产中你是如何确保消息不丢失的?
八、总结
RocketMQ 不承诺 "天然不丢消息",但通过:
同步发送 + 同步刷盘 + 同步主从 + 先业务后 ACK
可以实现工业级、金融级、生产级的消息不丢失。
记住一句话:消息不丢失,不是中间件单方面的事,是全链路设计的结果。