连载中 9/18

最大努力通知:通知到为止的艺术

2026-06-18 · 4747 阅读 · 0 评论 · 0 赞

宕机三小时的接收方

老王把平台能力开放给外部商户,支付成功后要把结果通知商户系统。第一版直接复用了内部 MQ 的可靠投递:无限重试直到成功。结果撞上商户系统升级宕机三小时——重试队列堆了八万条,告警炸锅,而对面的人还在等升级结束。这次折腾让老王想明白一件事:对外通知和内部消息是两种生物。内部服务自己可控,消息必达天经地义;外部系统你管不着,无限重试是自我感动。

模式定义:尽力喊,留后门

最大努力通知(Best Effort Delivery)的两个组成部分:

一:有上限的重试——按阶梯间隔通知有限次,比如 1 分钟、5 分钟、30 分钟、2 小时、6 小时各一次,共五次,全失败就停止。间隔递增是给接收方恢复留时间,上限是给自己止损。

二:主动查询的口子——这是精髓。通知不到不算完,发起方提供查询接口(或定期生成对账文件),接收方发现自己没收到,可以主动来查。通知是推,查询是拉,推拉结合,信息最终不丢。

微信支付、支付宝的异步回调就是教科书实现:商户接口没响应,按 15 秒、15 秒、30 秒、3 分钟、10 分钟……递减频率重试若干次,同时商户可以随时调查询接口对账。你在商场见过的这套回调,就是最大努力通知的工业级版本。

实现的三块积木

notify_task 表:业务ID, 目标URL, 已试次数, 下次时间, 状态
调度器:扫描到期任务 → 发通知 → 成功标记完成
        失败次数+1,按阶梯算下次时间
        超过上限 → 标记「通知失败」,转入人工/对账
查询接口:接收方按业务单号主动查最终状态

和本地消息表长得像,差别在语义:本地消息表的目标是「必达」,任务永不放弃;最大努力通知的目标是「尽力」,任务可以判死。判死不是躺平——通知失败的任务要进告警和对账流程,由对账系统兜底(第 16 篇),模式之间是接力关系。

内部与外部的分界线

对象方案原因
内部服务可靠消息(消息表/事务消息)自己可控,必达值得
外部商户/第三方最大努力通知 + 查询接口不可控,止损优先
短信/推送最大努力通知通道本身尽力而为

分界线划错了会发生什么:拿可靠消息对外部系统无限重试,是把自己的资源押给别人的故障;拿最大努力通知给内部服务,该到的积分没到,用户投诉。老王把这条线写进了服务边界规范:跨出自己团队的调用,默认按最大努力通知设计。

小结

最大努力通知一句话:阶梯重试尽力喊、喊不到判死、查询接口留后门;对内可靠消息,对外尽力而为,边界决定方案。消息型方案(消息表、事务消息、通知)到此讲完,它们全都默认一件事——接收方能正确处理重复消息。这个默认不成立时一切归零:下一篇把系列的通用地基补齐——幂等设计。

☕
503

10 年全栈工程师 · 503咖啡馆主理人

#分布式事务#最大努力通知#回调#通知重试#对账

评论 (0)

热门推荐

连载中 11/22

主从搭建实操:从零配出一主两从

光讲原理不过瘾?手把手搭一主两从:my.cnf 六个参数、复制账号、GTID、CHANGE REPLICATION SOURCE TO、SHOW REPLICA STATUS 验收,附翻车排查清单。

#MySQL#主从复制#GTID#主从搭建#高可用
2026-05-07 · 10101 阅读 · 0 评论 · 0 赞
连载中 16/22

连接池:HikariCP 参数与连接风暴

连接池不是越大越好:8 核机器配 1000 连接反而更慢的数学原理,HikariCP 四个必调参数,maxLifetime 与 wait_timeout 的隐形陷阱。

#MySQL#连接池#HikariCP#maxLifetime#连接风暴
2026-05-10 · 9873 阅读 · 0 评论 · 0 赞
连载中 4/16

缓存穿透:恶意 ID 打穿 MySQL 的四道防线

请求的数据在缓存和数据库里都不存在时,缓存形同虚设。聊聊参数校验、空值缓存、布隆过滤器、限流熔断四道防线的原理与组合打法。

#Redis#缓存穿透#布隆过滤器#高可用
2026-05-16 · 9294 阅读 · 21 评论 · 287 赞