最大努力通知,听起来有点"尽人事"的意味——我通知你,但不保证你一定收到。这其实是分布式事务的另一种思路,跟本地消息表、事务消息那种"死磕到底"的方式不一样。
今天我们结合微信支付的场景,把这个方案彻底聊透。
从微信支付说起
微信 Native 支付的流程里,有两个步骤值得特别关注。

用户在微信输入密码、支付完成之后,微信支付系统会往商家预设的 notify_url 推送支付结果。这个推送是异步的,而且如果商家系统没有回应,微信会反复通知多次。与此同时,商家系统也可以主动去调微信的查单接口,不必完全等着被动通知。
这两点就是最大努力通知的核心思路:我尽量通知你,你也可以主动来查。
这个方案要满足两点
从上面的例子可以提炼出最大努力通知方案的两个基本要求。
一是重复通知机制。通知方要有重试逻辑,并且最好是梯度递增的间隔,比如 1 分钟、5 分钟、10 分钟,不要一失败就放弃,也不要每秒狂轰。这里的"最大努力"体现在:我会不断重试,但我不保证你一定收到。
二是查询兜底机制。万一通知实在送不到,或者接收方想重新确认结果,通知方必须提供一个查询接口,让接收方主动来拉数据。这是整个方案的兜底。
用一句话总结:尽可能通知 + 提供查询接口,缺一不可。
它跟本地消息表、事务消息有什么本质区别
很多人会把最大努力通知和本地消息表、事务消息混在一起。这几个方案确实都涉及消息重试,但出发点不同。
本地消息表或者事务消息,可靠性由通知方兜底,消息一定要发出去、一定要送达,通知方不达目的不罢休。
最大努力通知里,通知方只是"尽力而为",接收方自己要有"主动查"的能力,可靠性的担子更多落在接收方身上。
这也就解释了为什么支付平台普遍采用这种模式——平台只负责推,至于你收没收到、处理没处理,商家自己也要做好轮询兜底。
两种具体实现方式
实际落地时,有两种常见方式,来看看各自的玩法。
MQ 重试
这个方式最简单,直接把最大努力通知这件事交给 MQ 的重试机制来干。
通知方往 MQ 发一条普通消息,不需要保证百分百发送成功——万一真没发出去,就靠接收方主动调查询接口兜底。接收方订阅这个 MQ,消费失败之后 MQ 自动重试,天然就具备了"多次通知"的能力。

这里的关键是:接收方自己在消费端做幂等,因为重试可能带来重复消费,同时通知方必须提供查询接口供接收方核查。
通知程序重试
这个方式更接近微信支付的实际设计——在 MQ 和接收方之间,多了一个专门的"通知程序"。
通知方发消息到 MQ,这里可以用本地消息表或者事务消息来保证原子性,也可以不用,转而把可靠性压到查询接口上。然后有个专门的通知程序监听这个 MQ,收到消息后通过 HTTP 等方式往接收方的回调地址推送,推送成功才算消费完成。

和第一种方式最大的区别在于:监听 MQ 的不是接收方,而是通知程序。通知程序充当了一个中间人的角色,专门负责把消息翻译成 HTTP 请求推给外部系统,接收方不需要接入 MQ,只要暴露一个回调地址就行。
这在对接外部系统时非常实用,毕竟你不能要求对方也来接你的 MQ。
最后
两种方案都能实现最大努力通知,核心逻辑是一样的。
真正不能省的,只有一件事:通知方必须提供查询接口。重试机制可以五花八门,但查询兜底是死规矩,有了它,整个方案才能真正闭环。