大多数集成是按项目做的:有人对好字段,测试通过,团队转去做别的,而这个集成接下来要跑上好几年。等到一张发票在两个系统之间消失,两边团队查各自的日志,都没看到报错,于是都认定问题在对方。
我们认为,对业务重要的集成应该像小产品一样运营:有用户、有公开接口、有健康指标,也有人负责。下面五点,是它与一段碰巧能跑的脚本的区别。
契约,不只是数据结构
把接口写成机器可校验的形式,HTTP用OpenAPI,事件用AsyncAPI加Schema注册中心。在流水线里按契约测试,最好采用消费者驱动的契约测试,让破坏性变更在构建时失败,而不是让客户的订单失败。
字段类型是容易的部分,真正伤人的分歧在于含义。契约还要回答:
- 这里的“客户”指什么,和另一个系统说的是一回事吗?
- 金额单位是分还是元,含不含GST?时间戳是UTC吗?
- 哪些字段可以为空,空值代表什么?
- 速率限制和预期流量是多少?
- 变更如何做版本管理,下线前提前多久通知消费方?
幂等
网络故障常常含糊不清。请求超时后,调用方不知道操作有没有执行,只好重试。多数消息代理提供的是至少一次投递,重复消息本来就是常态。没有幂等,一次普通的超时就可能变成重复扣款。
幂等是指重复执行多次与执行一次的效果相同。通常的做法是:
- 发送方为每个业务操作生成唯一键,每次重试都沿用。
- 接收方在同一个数据库事务里记录这个键并完成操作。分开做的话,中途崩溃就会产生重复。
- 收到已知的键时返回原结果,不再重复执行。
- 键的保留时间要长于最长的重试周期。
不添乱的重试
简单粗暴的重试循环会让所有客户端一起涌上去,把本就吃力的服务彻底压垮。
- 只重试以后可能成功的情况:超时、
429和大多数5xx。400重复多少次也不会变好。 - 使用带抖动的指数退避,让客户端在时间上分散开。
- 遵守
Retry-After响应头。 - 限制重试次数和总时长,长时间故障时可考虑熔断器。
- 仍然失败的消息进入死信队列,并确保它有告警、有负责人、有重放工具。没人看的死信队列只是慢慢丢数据。
顺序问题也要明确决定。如果重试时更新可能跑到创建前面,接收方就需要版本号或时间戳来应对。
业务看得懂的可观测性
一屏绿色的HTTP状态码说明不了多少。集成可以整天返回200,同时悄悄丢掉所有带某个税码的订单。要度量业务认得的东西:
- 对账: 定期比较从系统A发出和到达系统B的订单数,能抓到任何错误日志都抓不到的问题。
- 新鲜度: 最早一条未处理消息的等待时间,以及距上次成功同步多久。
- 关联: 每一跳都带上同一个ID,一笔交易可以贯穿所有相关系统的日志。
- 失败队列: 卡住了多少条,卡了多久。
负责人
每个集成都要有明确的团队端到端负责,包括最后查出来问题在对方的故障。这个团队维护契约、运行手册、告警和消费方清单,也要有维护预算,因为上游API和凭据总在变化。
如果说不出负责人是谁,这就是你最大的集成风险。没人管的凭据就是常见的例子:一把即将过期的API密钥,配置它的人早已离职。
本周可以做的事
挑一个悄悄停掉后损失最大的集成,回答这些问题:
- 它停了谁会收到告警,多快能知道?
- 同一条消息到达两次会怎样?
- 失败的消息去了哪里,今天有多少条?
- 上次有人比对两边的记录数是什么时候?
- 凭据归谁管,何时过期?
答不上来的问题就是你的待办清单。可以先做每日对账报告,通常成本不高,也很快就能看出有没有问题。