一、消息不丢失机制
1. 消息丢失的核心环节
消息链路中 4 个关键丢失风险点,跨网络传输和缓存异步刷盘是主要场景:

2. 生产者端防丢失方案
(1)发送确认机制
| 发送方式 | 特点 | 代码示例 |
|---|---|---|
| 单向发送 | 效率高,可能丢消息 | producer.sendOneway(msg); |
| 同步发送 | 安全可靠,效率低 | SendResult result = producer.send(msg, 20000); |
| 异步发送 | 安全与效率平衡 | producer.send(msg, new SendCallback() {<br>@Override<br>public void onSuccess(SendResult res) {}<br>@Override<br>public void onException(Throwable e) {}<br>}); |
(2)事务消息机制(RocketMQ 特有)
核心流程:

适用场景:电商订单创建与支付确认等分布式事务场景
3. Broker 端防丢失方案
(1)刷盘机制
- 同步刷盘(SYNC_FLUSH):写入日志即调用 fsync,消息不丢失但 IO 压力大
- 异步刷盘(ASYNC_FLUSH):定期刷盘,性能好但断电可能丢缓存消息
- 核心配置:
flushDiskType=SYNC_FLUSH(生产环境推荐)
(2)主从同步机制
| 集群类型 | 同步方式 | 数据安全性 | 切换逻辑 |
|---|---|---|---|
| 普通集群 | 同步 Master(SYNC_MASTER)/ 异步 Master(ASYNC_MASTER) | 高(SYNC_MASTER) | Master 挂了不自动切换 |
| DLedger 集群 | Raft 协议多数派确认 | 极高 | 自动选举 Leader,数据一致性优先 |
关键差异:Kafka Leader 崩溃后新 Leader 以自身数据为准,可能丢失未同步消息;RocketMQ Master 重启后继续同步未同步数据,不丢失
4. 消费者端防丢失方案
-
核心原则:先处理消息,再提交消费确认
-
风险场景:异步处理消息时提前返回成功状态
// 错误示例(可能丢失消息)
consumer.registerMessageListener((msgs, context) -> {
new Thread(() -> {
// 异步处理业务,可能未执行完就返回成功
}).start();
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
});// 正确示例(同步处理)
consumer.registerMessageListener((msgs, context) -> {
// 同步处理业务逻辑
processBusiness(msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}); -
重试机制:未收到确认时 Broker 会重复投递,RocketMQ 通过 Offset,RabbitMQ 通过重新入队
5. 极端场景(MQ 集群全挂)处理
- 降级方案:生产者发送失败时写入本地缓存(如 Redis、文件)
- 恢复机制:独立线程循环重试发送缓存消息,MQ 恢复后自动补送
6. 零丢失方案总结(trade-off)
| 环节 | 方案 | 代价 |
|---|---|---|
| 生产者 | 同步发送 + 重试 | 降低吞吐 |
| Broker | 同步刷盘 + DLedger 集群 | IO / 网络负担增加 |
| 消费者 | 同步处理 + 确认 | 无法异步提升效率 |
| 集群故障 | 降级缓存 | 增加存储成本 |
二、消息顺序性保障
1. 核心概念
- 局部有序:同一业务维度(如同一订单)的消息有序(实际业务常用)
- 全局有序:整个 Topic 的消息有序(极少场景需要,性能极差)
2. 实现机制(RocketMQ)

两大关键:
- 生产者:通过分区算法将同业务标识消息写入同一 MessageQueue
- 消费者:单个 Consumer 线程对应单个 MessageQueue,串行消费
3. 不同 MQ 对比
| MQ 产品 | 顺序性保障方式 | 特点 |
|---|---|---|
| RocketMQ | 分区绑定 + 单线程消费 | 支持局部有序,配置灵活 |
| Kafka | 单 Partition 单线程消费 | 天生支持局部有序 |
| RabbitMQ | 单 Queue 对应单 Consumer | 经典队列需手动保证绑定关系 |
三、消息幂等性处理
1. 幂等性定义
- 多次处理同一消息,业务结果一致(不重复创建订单、不重复扣款)
2. 生产者端幂等
(1)RocketMQ 实现
- 自动生成唯一
messageId(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX) - Broker 通过该 ID 判断重复消息
(2)Kafka 实现
- 开启幂等性配置(默认开启),核心机制:
- PID:生产者唯一标识
- Sequence Number:每个 PID+Partition 的单调递增序号
- Broker 仅接收序号 = 当前 SN+1 的消息
3. 消费者端幂等
(1)去重依据(优先级)
- 业务唯一标识(推荐):如订单 ID、支付流水号(通过
msg.setKey(业务ID)设置) - MQ 内置唯一 ID:
messageId(批量 / 事务消息场景可能失效)
(2)实现方式
// 伪代码示例
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs) {
for (MessageExt msg : msgs) {
String businessId = msg.getKey(); // 订单ID
// 1.查缓存/数据库判断是否已处理
if (isProcessed(businessId)) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
// 2.处理业务逻辑
processBusiness(msg);
// 3.标记为已处理(缓存/数据库)
markAsProcessed(businessId);
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
(3)异常处理
- 重试队列:重复投递的消息进入重试队列(
%RETRY%消费者组名) - 死信队列:多次重试失败进入死信队列(
%DLQ%消费者组名),需手动调整权限后处理
四、消息积压处理
1. 积压风险
- RocketMQ/Kafka:日志文件过期会删除未消费消息
- RabbitMQ:经典队列积压严重影响服务端性能
2. 处理方案
(1)临时扩容(快速见效)
- 核心限制:Consumer 实例数 ≤ MessageQueue 数(RocketMQ/Kafka)
- 操作步骤:
- 增加 Consumer 实例(不超过 Queue 数)
- 优化 Consumer 消费逻辑(关闭非核心业务、异步处理非关键步骤)
(2)分流处理(大量积压时)

- 操作步骤:
- 创建新 Topic,配置更多 MessageQueue
- 部署临时消费者,仅负责将旧 Topic 消息转存到新 Topic
- 新 Topic 部署足够多 Consumer 实例快速消费
- 消费完成后恢复正常架构
五、常见面试题及参考答案
1. RocketMQ 如何保证消息不丢失?
- 生产者:使用同步发送 + 重试机制,关键场景用事务消息
- Broker:配置同步刷盘 + DLedger 高可用集群
- 消费者:同步处理消息后提交确认,避免异步提前返回成功
- 极端情况:降级缓存 + 后台重试发送
2. 如何保证消息的顺序性?
- 实现局部有序:生产者将同业务标识消息写入同一 MessageQueue,消费者单线程消费单个 MessageQueue
- 不推荐全局有序:Topic 只配置 1 个 Queue,性能极差,无实际业务价值
3. 如何解决消息重复消费问题?
- 生产者端:RocketMQ 通过唯一 messageId 去重,Kafka 通过 PID+SequenceNumber 保证幂等
- 消费者端:基于业务唯一标识(如订单 ID)做幂等处理,通过缓存 / 数据库记录处理状态
4. 消息积压了怎么处理?
- 短期:增加 Consumer 实例(不超过 Queue 数),优化消费逻辑
- 长期:创建多 Queue 新 Topic,临时分流积压消息,批量快速消费
5. RocketMQ 的事务消息原理是什么?
- 两阶段提交:先发送半消息,执行本地事务后再确认 Commit/Rollback
- 兜底机制:Broker 定期回查本地事务状态,避免事务结果丢失
6. RocketMQ 与 Kafka 在消息安全上的核心差异?
- 主从同步:RocketMQ Master 挂了不切换,重启后同步未同步数据;Kafka 会选举新 Leader,丢失未同步消息
- 设计初衷:RocketMQ 源于金融场景,优先数据安全;Kafka 源于日志收集,优先服务可用性