🚀 RocketMQ如何实现消息Exactly-Once语义?
🔍 核心实现原理
RocketMQ通过生产者幂等性 + 消费者幂等性 + 事务消息三重保障机制实现准Exactly-Once语义(需业务配合)
🛠️ 生产者端保障措施
1. 消息发送幂等性(Idempotent Producer)
java
// 使用Message的UNIQUE_KEY设置业务唯一标识
Message msg = new Message("OrderTopic",
"20230815-ORDER-10086".getBytes()); // 业务唯一ID作为消息Key
msg.setKeys("ORDER_10086"); // 设置唯一消息Key
SendResult sendResult = producer.send(msg);
实现原理:
- 🌟 通过
keys
字段设置业务唯一标识 - 🔑 Broker端会基于MessageID + Key进行重复判断
- ⏱️ 默认保留10分钟重复检查(可通过
transactionTimeout
配置)
🛒 消费者端保障措施
2. 消费幂等设计
scss
consumer.registerMessageListener((MessageListenerOrderly) (msgs, context) -> {
for (MessageExt msg : msgs) {
String orderId = msg.getKeys(); // 获取业务唯一标识
if (isProcessed(orderId)) { // 检查本地去重表
return ConsumeOrderlyStatus.SUCCESS;
}
// 业务处理逻辑
processOrder(orderId);
// 记录已处理标识
markAsProcessed(orderId);
}
return ConsumeOrderlyStatus.SUCCESS;
});
实现要点:
- 🧰 建立业务去重表(Redis/DB)记录处理状态
- ⚡ 使用
UNIQUE KEY
约束防止重复插入 - 🔄 消息重试时跳过已处理记录
💼 事务消息机制
typescript
TransactionMQProducer producer = new TransactionMQProducer("GROUP");
producer.setTransactionListener(new TransactionListener() {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
try {
// 执行本地事务
boolean success = bizService.process(msg);
return success ? LocalTransactionState.COMMIT_MESSAGE :
LocalTransactionState.ROLLBACK_MESSAGE;
} catch (Exception e) {
return LocalTransactionState.UNKNOW;
}
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
return bizService.checkTransactionStatus(msg) ?
LocalTransactionState.COMMIT_MESSAGE :
LocalTransactionState.ROLLBACK_MESSAGE;
}
});
事务流程:
- 📨 发送半消息(对消费者不可见)
- ✅ 执行本地事务
- 🧑💻 根据结果提交/回滚消息
- 🔄 事务回查机制兜底(默认15次检查)
🧩 终极方案:分布式锁+幂等表
typescript
public void handleMessage(MessageExt msg) {
String lockKey = "MSG_LOCK:" + msg.getMsgId();
try {
// 获取分布式锁
if (redisLock.tryLock(lockKey, 30)) {
if (dupCheckService.isProcessed(msg.getKeys())) {
return;
}
// 业务处理
processBusiness(msg);
// 记录处理状态
dupCheckService.markProcessed(msg.getKeys());
}
} finally {
redisLock.unlock(lockKey);
}
}
优势组合:
- 🔒 分布式锁保证并发安全
- 📝 幂等表持久化处理状态
- ⏳ 锁自动过期防止死锁
📌 总结要点
- 🚫 RocketMQ无法100%保证Exactly-Once(需业务配合)
- 🔗 生产端:消息Key+事务消息机制
- 🛡️ 消费端:分布式锁+幂等表组合拳