本文目录导读:

- 案例一:支付宝/微信支付回调(最经典)
- 案例二:银行代扣 / 转账结果通知
- 案例三:第三方登录(如QQ/微信登录的授权回调)
- 案例四:物流状态同步(快递100 / 菜鸟)
- 具体代码逻辑示例(伪代码/概念示意)
- 最大努力通知的特征
最大努力通知是分布式事务中一种最终一致性的解决方案,它的核心思想是:发起通知方(本地事务)尽可能地将事件结果通知给接收方,如果接收方未确认,则不断地重试,直到成功或达到重试上限。
它与可靠消息最终一致性方案的区别在于,最大努力通知不保证100%成功,也不要求接收方必须消费消息,它只负责“尽力而为”,它通常适用于对数据一致性要求不那么严格(如非核心链路)或跨机构(无法使用MQ)的业务场景。
以下是几个经典的最大努力通知案例:
支付宝/微信支付回调(最经典)
场景描述: 用户在电商平台下单,跳转到支付宝或微信支付,支付成功后,支付宝(或微信)需要通知电商平台的后台系统“这笔订单已经支付成功”,以便平台发货。
为什么用最大努力通知?
- 支付宝是第三方机构,电商平台无法部署支付宝的消息队列(MQ),也无法监控支付宝的内部事务。
- 支付宝只需要保证“通知到了”或者“通知不到就多次重试”,至于电商平台是否成功处理(比如是否成功更新库存),支付宝并不关心,只要平台返回了“处理成功”的标志,支付宝就不再重复通知。
执行流程(最大努力通知的典型过程):
- 第一阶段(正常通知): 用户支付完成,支付宝生成支付结果。
- 第二阶段(主动通知-尽力): 支付宝调用电商平台提供的回调接口(HTTP POST),发送支付成功的消息。
- 电商平台收到后,处理本地业务(修改订单状态),返回“success”字符串。
- 支付宝收到“success”,停止通知。
- 第三阶段(定时补偿-重试): 如果支付宝没有收到“success”(网络超时、接口异常、平台宕机),支付宝会启动定时任务。
- 按照一定的策略(15分钟、15分钟、30分钟、3分钟、10分钟…)进行间隔递增的重试。
- 重试次数有限制(例如最多重试24次)。
- 第四阶段(人工兜底/对账): 如果重试了24次依然失败(比如平台服务器停机了一整天),支付宝放弃通知(尽力了),电商平台必须通过主动查询接口(主动拉取订单状态)或人工对账来弥补这笔漏单,这也是为什么支付接口要提供“查询订单状态”API的原因。
该案例中的要点:
- 发起方(支付宝):只负责推送和重试,不保证接收方一定处理成功。
- 接收方(电商平台):必须提供幂等接口(重复通知不会导致重复发货),且需要主动查询作为兜底。
银行代扣 / 转账结果通知
场景描述: 电商平台与某银行合作,进行用户账户的余额代扣,银行扣款成功后,需要将“扣款成功”的结果发给电商平台。
为什么用最大努力通知? 银行系统的可靠性要求极高,往往处于内网隔离状态,不允许外部系统直接接入银行内部MQ,银行通常只对外提供基于WebService或HTTP的开放接口,对于银行来说,只要把结果发出来了,就完成了“最大努力通知”。
执行流程:
- 电商平台调用银行API发起代扣。
- 银行扣款成功,记录扣款流水。
- 银行系统异步调用电商平台提供的回调地址。
- 若回调失败,银行内部定时任务开始重试(初始间隔短,随后变长)。
- 若最终重试失败,银行停止通知,电商平台需要在第二天跑批时,通过“银行文件”或“查询接口”核对流水,发现这笔未确认的扣款,主动补单。
第三方登录(如QQ/微信登录的授权回调)
场景描述: 用户在APP点击“微信登录”,跳转至微信授权页,用户确认后,微信服务器将授权码或用户信息通过302跳转回调给APP服务器。
为什么用最大努力通知? 如果APP服务器在接收到回调时刚好宕机,微信服务器会放弃通知(它只会通知一次,没有重试机制),APP客户端会一直等待,但由于微信没有重试机制,APP这边就需要主动拉取用户信息(主动查询)。
对比分析: 这个案例是“最大努力通知”的退化版,因为很多开放平台(如OAuth2.0协议)规定回调只发一次,没有重试机制,一旦失败,只能靠APP前端定时轮询后端接口来触发补偿,这同样体现了“尽力而为,不保证成功”的核心思想。
物流状态同步(快递100 / 菜鸟)
场景描述: 电商平台对接第三方物流平台(如快递100),用户下单后,物流轨迹信息发生变化(揽收、运输、派送、签收),物流平台需要将最新的轨迹推送给电商平台。
为什么用最大努力通知? 物流数据量巨大,且实时性要求不高(晚几分钟没关系),但绝对不允许数据丢失导致用户体验差,物流平台会定期推送,如果推送失败,只会重试几次,如果一直失败,电商平台只能通过定时任务去物流平台拉取最新轨迹来兜底。
具体代码逻辑示例(伪代码/概念示意)
以下以一个模拟支付回调的后端处理逻辑,展示“最大努力通知”中接收方(电商平台)如何配合。
// 版本一:接收方(电商平台)的回调接口(需要满足幂等性)
@RestController
public class PayCallbackController {
@Autowired
private OrderService orderService;
/**
* 支付宝/微信回调我们的接口
* 要求:处理完业务后,必须返回"SUCCESS"给支付平台,否则支付平台会重试
*/
@PostMapping("/api/pay/notify")
public String handlePayNotify(@RequestBody Map<String, String> params) {
// 1. 验签(保证是支付平台发的)
boolean signOk = payService.verifySign(params);
if (!signOk) {
// 验签失败,直接返回FAILURE,让支付平台重试(虽然可能重试也没用,但会导致一直重试)
return "FAILURE";
}
// 2. 根据订单号查询本地订单,判断订单状态
String orderId = params.get("orderId");
Order order = orderService.getByOrderId(orderId);
// 3. 幂等性检查:如果订单状态已经是"已支付",说明之前处理过了,直接返回成功,避免重复发货
if ("PAID".equals(order.getStatus())) {
return "SUCCESS";
}
// 4. 执行业务(修改订单状态、扣库存)
orderService.markOrderPaid(orderId);
// 5. 处理成功,告诉支付平台别再调用我了
// 注意:这里如果扣库存失败,我们应该返回"FAILURE"让支付平台重试
return "SUCCESS";
}
}
最大努力通知的特征
| 特征维度 | 说明 |
|---|---|
| 保证性 | 不保证100%送达,只保证“尽力”。 |
| 实时性 | 有一定的延迟,依赖定时任务的重试间隔。 |
| 业务敏感性 | 适用于非实时、非核心、跨机构(无法用MQ)的场景。 |
| 可靠性依赖 | 必须依赖接收方主动查询(兜底) 或对账系统来保证最终一致性。 |
| 技术实现 | 通常是 HTTP/WebService调用 + 定时任务重试 + 状态标志位,不依赖MQ中间件(或仅用MQ辅助解耦)。 |
在实际微服务架构中,最大努力通知通常用于处理“回调类”业务,如果是公司内部系统,更推荐的还是RabbitMQ/Kafka等可靠消息中间件,但一旦涉及和外部(银行、第三方支付、物流)对接,最大努力通知几乎是唯一的选择。