宕机三小时的接收方
老王把平台能力开放给外部商户,支付成功后要把结果通知商户系统。第一版直接复用了内部 MQ 的可靠投递:无限重试直到成功。结果撞上商户系统升级宕机三小时——重试队列堆了八万条,告警炸锅,而对面的人还在等升级结束。这次折腾让老王想明白一件事:对外通知和内部消息是两种生物。内部服务自己可控,消息必达天经地义;外部系统你管不着,无限重试是自我感动。
模式定义:尽力喊,留后门
最大努力通知(Best Effort Delivery)的两个组成部分:
一:有上限的重试——按阶梯间隔通知有限次,比如 1 分钟、5 分钟、30 分钟、2 小时、6 小时各一次,共五次,全失败就停止。间隔递增是给接收方恢复留时间,上限是给自己止损。
二:主动查询的口子——这是精髓。通知不到不算完,发起方提供查询接口(或定期生成对账文件),接收方发现自己没收到,可以主动来查。通知是推,查询是拉,推拉结合,信息最终不丢。
微信支付、支付宝的异步回调就是教科书实现:商户接口没响应,按 15 秒、15 秒、30 秒、3 分钟、10 分钟……递减频率重试若干次,同时商户可以随时调查询接口对账。你在商场见过的这套回调,就是最大努力通知的工业级版本。
实现的三块积木
notify_task 表:业务ID, 目标URL, 已试次数, 下次时间, 状态
调度器:扫描到期任务 → 发通知 → 成功标记完成
失败次数+1,按阶梯算下次时间
超过上限 → 标记「通知失败」,转入人工/对账
查询接口:接收方按业务单号主动查最终状态和本地消息表长得像,差别在语义:本地消息表的目标是「必达」,任务永不放弃;最大努力通知的目标是「尽力」,任务可以判死。判死不是躺平——通知失败的任务要进告警和对账流程,由对账系统兜底(第 16 篇),模式之间是接力关系。
内部与外部的分界线
| 对象 | 方案 | 原因 |
|---|---|---|
| 内部服务 | 可靠消息(消息表/事务消息) | 自己可控,必达值得 |
| 外部商户/第三方 | 最大努力通知 + 查询接口 | 不可控,止损优先 |
| 短信/推送 | 最大努力通知 | 通道本身尽力而为 |
分界线划错了会发生什么:拿可靠消息对外部系统无限重试,是把自己的资源押给别人的故障;拿最大努力通知给内部服务,该到的积分没到,用户投诉。老王把这条线写进了服务边界规范:跨出自己团队的调用,默认按最大努力通知设计。
小结
最大努力通知一句话:阶梯重试尽力喊、喊不到判死、查询接口留后门;对内可靠消息,对外尽力而为,边界决定方案。消息型方案(消息表、事务消息、通知)到此讲完,它们全都默认一件事——接收方能正确处理重复消息。这个默认不成立时一切归零:下一篇把系列的通用地基补齐——幂等设计。
评论 (0)