架构思维:通用架构模式_怀疑下游的设计思路与最佳实践
文章目录
- 1. 引言
- 2. 为什么要“怀疑下游”
- 3. 三大类下游依赖及应对方案
- 3.1 对其他微服务的依赖
- 3.1.1 分布式事务简易补偿方案
- 3.2 对数据库的依赖
- 3.3 对消息中间件的依赖
- 4. 分布式事务实战案例
- 5. 小结
1. 引言
在
架构思维:通用架构模式_从设计到代码构建稳如磐石的系统和架构思维:通用架构模式_稳如老狗的SDK设计最佳实践中,我们从微服务对外接口和消息消费,以及服务自身编码规范,分别阐述了“防备上游、做好自己”的两大准则落地思路。
接下来我们将继续深入,聊一聊为什么要“怀疑下游”,并展示落地手段;同时,针对拆分后带来的分布式事务与异步消息问题,提供一个无需 TCC/Saga 框架的简单实战方案。
2. 为什么要“怀疑下游”
定义“怀疑下游”:在设计之初,就假设所有下游都会偶发故障,提前为各种失效场景设计补偿和降级措施。
如图所示,微服务往往依赖众多其他服务、数据库、缓存和消息中间件,这些下游系统可能因代码 Bug、网络抖动、磁盘故障或运维失误而不可用。一旦下游依赖不可用,就会导致本服务接口可用率下降,甚至整体宕机。
3. 三大类下游依赖及应对方案
下游依赖大致分为:RPC 服务、数据库和消息中间件。
3.1 对其他微服务的依赖
在提单时,订单模块需要调用库存模块进行商品的扣减,以便判断用户购买的商品是否有货。订单调用库存的扣减接口会有以下几种情况发生。
- 调用库存接口返回成功且库存数量充足,订单模块便将此用户订单保存至数据库,并返回用户下单成功消息。
- 调用库存接口返回成功且库存数量充足,但订单模块将此用户订单保存至数据库时出错并进行数据库回滚,同时订单模块返回用户下单失败。
- 调用库存接口超时,订单模块判断此次调用库存接口失败,返回用户下单失败。
…
-
高可用治理:依赖后置、并行化调用、超时与重试、服务降级等手段,同样适用于读/写场景。具体请移步 架构思维:构建随时可写的高可用写服务_链路依赖管控
-
分布式事务:拆单后的下单场景中,订单和库存是两个独立微服务。调用库存扣减接口后,本地 DB 可能因异常回滚,造成库存与订单状态不一致。
3.1.1 分布式事务简易补偿方案
在第 2 点里,
- 调用库存接口返回成功且库存数量充足,但订单模块将此用户订单保存至数据库时出错并进行数据库回滚,同时订单模块返回用户下单失败。
因为订单模块本地的数据库事务回滚了,但调用库存接口产生的已扣减的商品数量并没有回滚,此时就会导致库存数据少于实际的数据。
有一些基于 TCC 和 Saga 的成熟基础框架可以解决上述分布式事务问题,但理解和接入成本较高。此处介绍一种本质上和 TCC、Saga 理论相类似,但无须借助第三方框架的简单、易落地的解决方案。理解此方案也有助于理解 TCC 和 Saga 的思想
“本地任务表+异步 Worker”架构
- 预写任务:接单后,先在任务表写入订单号、商品、数量、用户等信息。
- 调用库存扣减:
- 成功且本地写单事务成功:标记任务“已成功”;
- 接口失败或超时:标记任务“待回滚”,同步返回下单失败;
- 本地写单失败:任务保留“初始化”,直接返回失败。
- 异步补偿:Worker 定时扫描任务表,对“待回滚”且超时未更新的任务,调用库存返还接口,保证最终一致性。
通过上述方式,能够将各种失败场景里漏返回的商品数量进行返还,保证库存数量的最终一致性,完成分布式事务。上述保障数据最终一致性主要是依赖任务表和订单表在同一个数据库里,可以通过本地事务来保障订单表数据写入成功后,任务表里的任务状态绝对能够更新为“已成功”。而当提单失败后,任务表的状态为“非成功”状态,再通过类似 TCC 和 Saga 的异步补偿性 Worker 来进行业务回滚即可保证最终最一致性。
在发起分布式事务的业务模块的数据库里创建补偿性任务,基本上可以复用在其他存在分布式事务的场景里。如果不希望引入更加复杂的 TCC 和 Saga 框架,可以尝试利用此方式来解决架构微服务化之后带来的分布式事务的问题。
3.2 对数据库的依赖
- 原则 1:主从库跨机房部署,测试环境也需主从复制,以防磁盘损坏带来巨大恢复成本。
- 原则 2:SQL 尽量简单清晰,避免多层嵌套语句难以调试。
- 原则 3:业务迭代快时,谨慎使用外键,防止隐藏级联风险与变更疏漏。
- 原则 4:避免超长 varchar 存 JSON;并发更新时容易丢失修改,若需存复杂结构,可考虑拆表或并发锁。
3.3 对消息中间件的依赖
- 原则 1:先写库/缓存,再发送消息,确保消费方反查时数据可见。
- 原则 2:消息携带版本号或高精度时间戳,支持乱序消费时判断先后。
- 原则 3:消息尽量全量,减少消费方反查接口带来的耦合与延迟。
- 原则 4:标记字段变更标识,避免无效通知引发误执行。
4. 分布式事务实战案例
结合第 3.1 节所述,示例流程:
5. 小结
我们围绕“怀疑下游”原则,介绍了针对 RPC 服务、数据库和消息中间件的治理要点;并通过“本地任务表+异步补偿”方案,演示了无需框架的分布式事务落地路径。 希望能提供一些帮助和思路。