“最大努力通知”(Maximum Effort Notification, MEN)是分布式事务中的一种最终一致性方案,在一些业务对一致性要求不是“强一致性”而是“最终一致性”的场景下,这种方式非常常见,尤其适用于异步通知类业务,比如:
- 支付回调通知
- 订单状态通知
- 第三方接口调用通知(如发短信、微信通知等)
一、核心思想概览
最大努力通知的核心思想是:
“只要有可能通知成功,就尽量通知;通知失败时,不放弃,持续尝试。”
它不追求立刻成功,也不会在失败后立即放弃,而是通过重试机制**“最大努力地”将结果通知到下游系统,保证最终能够成功,达到最终一致性**。
二、与其他分布式事务方案的比较
方案 | 事务模型 | 是否强一致性 | 示例场景 |
---|---|---|---|
2PC(两阶段提交) | 原子性 | ✅ 是 | 银行转账等金融交易 |
TCC | 业务级预留资源 | ✅ 是 | 机票、酒店预订 |
SAGA | 补偿事务 | ❌ 否 | 电商订单分布处理 |
最大努力通知(MEN) | 最终一致性通知 | ❌ 否 | 支付后通知商户发货,短信通知 |
三、最大努力通知的关键流程
1. 事务操作成功
比如你调用了一个“支付完成”接口,完成了本地数据库操作,或已经确认业务处理成功。
2. 通知外部系统
通知其他服务(如订单系统、物流系统、第三方平台),通常通过 HTTP、MQ、Webhook 等方式。
3. 若通知失败,进行重试
失败不放弃,按一定策略进行重试,比如:
- 固定时间间隔重试(每5分钟一次)
- 指数退避策略(5s、30s、1min、5min…)
- 最多重试N次或在24小时内持续尝试
4. 重试成功即终止;最终都失败则记录人工干预
比如:
- 入库“死信表”
- 报警通知人工介入
四、示意图
业务系统 通知系统(下游)
| |
|----本地业务成功---------->|
| |
|<-- 发送通知(如 HTTP)----|
| |
|<-- 接收成功:终止尝试 |
|<-- 接收失败:开始重试 ----|
|--- 重试策略(直到成功)--->|
五、关键组件和实现机制
✅ 重试队列或消息机制
- 基于数据库轮询(定时扫描失败记录)
- 基于消息队列(如RabbitMQ死信队列+延迟重发)
- 使用 Redis/Zookeeper/Quartz 等定时任务
✅ 幂等性设计(必须!)
由于通知会多次发送,下游系统必须能够处理重复的消息,常见手段:
- 请求中携带唯一业务ID
- 下游系统处理记录业务ID,若已处理则忽略重复通知
✅ 日志记录和告警机制
- 每次通知都要记录状态(成功、失败、重试次数)
- 超过最大重试次数,记录失败日志并报警
六、实际场景举例:微信支付回调
-
微信支付系统调用商户的回调URL
-
如果商户接口返回 HTTP 200 且内容是 “success” 则认为成功
-
否则微信会进行 频繁重试:
- 第1分钟每隔15秒一次
- 第1小时每隔1分钟一次
- 最长重试24小时
⚠️ 商户系统必须:
- 实现 幂等性处理
- 记录每次回调请求,避免重复处理
七、优缺点总结
优点 | 说明 |
---|---|
简单易实现 | 无需协调事务管理器 |
可扩展 | 易于集群部署,基于 MQ/定时任务 |
异步高性能 | 不阻塞主流程,适合非核心业务 |
缺点 | 说明 |
---|---|
无法强一致 | 下游系统长时间不可用会导致数据差异 |
需要幂等设计 | 否则重复通知会引发错误 |
可见性差 | 重试过程需日志记录和监控系统支撑 |
八、适用场景总结
最大努力通知适用于 非核心链路、允许最终一致性 的业务,比如:
- 支付结果通知(通知商户/用户)
- 订单状态同步
- 第三方系统通知(物流系统、消息推送等)
- 消息发送确认(邮件、短信、推送)
九、一个简单实现示例(伪代码)
// 假设我们使用数据库保存通知记录
class NotifyTask {
String id;
String targetUrl;
String payload;
int retryCount;
LocalDateTime nextTryTime;
boolean success;
}
// 定时任务,每隔一分钟执行
public void retryNotify() {
List<NotifyTask> tasks = db.getPendingTasks();
for (NotifyTask task : tasks) {
boolean ok = http.post(task.targetUrl, task.payload);
if (ok) {
task.success = true;
} else {
task.retryCount++;
task.nextTryTime = LocalDateTime.now().plusMinutes(5);
}
db.save(task);
}
}
十、总结一句话
最大努力通知方案是一种典型的 “异步、可重试、幂等保障” 架构,虽然无法实现强一致性,但它能以简单方式实现最终一致性,在很多互联网场景中非常实用。