当前位置: 首页 > backend >正文

架构思维:通用架构模式_怀疑下游的设计思路与最佳实践

文章目录

  • 1. 引言
  • 2. 为什么要“怀疑下游”
  • 3. 三大类下游依赖及应对方案
    • 3.1 对其他微服务的依赖
    • 3.1.1 分布式事务简易补偿方案
    • 3.2 对数据库的依赖
    • 3.3 对消息中间件的依赖
  • 4. 分布式事务实战案例
  • 5. 小结

在这里插入图片描述


1. 引言


架构思维:通用架构模式_从设计到代码构建稳如磐石的系统和架构思维:通用架构模式_稳如老狗的SDK设计最佳实践中,我们从微服务对外接口和消息消费,以及服务自身编码规范,分别阐述了“防备上游、做好自己”的两大准则落地思路。

接下来我们将继续深入,聊一聊为什么要“怀疑下游”,并展示落地手段;同时,针对拆分后带来的分布式事务与异步消息问题,提供一个无需 TCC/Saga 框架的简单实战方案。


2. 为什么要“怀疑下游”

定义“怀疑下游”:在设计之初,就假设所有下游都会偶发故障,提前为各种失效场景设计补偿和降级措施。

在这里插入图片描述

如图所示,微服务往往依赖众多其他服务、数据库、缓存和消息中间件,这些下游系统可能因代码 Bug、网络抖动、磁盘故障或运维失误而不可用。一旦下游依赖不可用,就会导致本服务接口可用率下降,甚至整体宕机。

3. 三大类下游依赖及应对方案

下游依赖大致分为:RPC 服务、数据库和消息中间件。

3.1 对其他微服务的依赖

在这里插入图片描述

在提单时,订单模块需要调用库存模块进行商品的扣减,以便判断用户购买的商品是否有货。订单调用库存的扣减接口会有以下几种情况发生。

  1. 调用库存接口返回成功且库存数量充足,订单模块便将此用户订单保存至数据库,并返回用户下单成功消息。
  1. 调用库存接口返回成功且库存数量充足,但订单模块将此用户订单保存至数据库时出错并进行数据库回滚,同时订单模块返回用户下单失败。
  1. 调用库存接口超时,订单模块判断此次调用库存接口失败,返回用户下单失败。

  • 高可用治理:依赖后置、并行化调用、超时与重试、服务降级等手段,同样适用于读/写场景。具体请移步 架构思维:构建随时可写的高可用写服务_链路依赖管控

  • 分布式事务:拆单后的下单场景中,订单和库存是两个独立微服务。调用库存扣减接口后,本地 DB 可能因异常回滚,造成库存与订单状态不一致。


3.1.1 分布式事务简易补偿方案

在第 2 点里,

  1. 调用库存接口返回成功且库存数量充足,但订单模块将此用户订单保存至数据库时出错并进行数据库回滚,同时订单模块返回用户下单失败。

因为订单模块本地的数据库事务回滚了,但调用库存接口产生的已扣减的商品数量并没有回滚,此时就会导致库存数据少于实际的数据。

有一些基于 TCC 和 Saga 的成熟基础框架可以解决上述分布式事务问题,但理解和接入成本较高。此处介绍一种本质上和 TCC、Saga 理论相类似,但无须借助第三方框架的简单、易落地的解决方案。理解此方案也有助于理解 TCC 和 Saga 的思想

“本地任务表+异步 Worker”架构

在这里插入图片描述

  1. 预写任务:接单后,先在任务表写入订单号、商品、数量、用户等信息。
  2. 调用库存扣减
    • 成功且本地写单事务成功:标记任务“已成功”;
    • 接口失败或超时:标记任务“待回滚”,同步返回下单失败;
    • 本地写单失败:任务保留“初始化”,直接返回失败。
  3. 异步补偿:Worker 定时扫描任务表,对“待回滚”且超时未更新的任务,调用库存返还接口,保证最终一致性。

通过上述方式,能够将各种失败场景里漏返回的商品数量进行返还,保证库存数量的最终一致性,完成分布式事务。上述保障数据最终一致性主要是依赖任务表和订单表在同一个数据库里,可以通过本地事务来保障订单表数据写入成功后,任务表里的任务状态绝对能够更新为“已成功”。而当提单失败后,任务表的状态为“非成功”状态,再通过类似 TCC 和 Saga 的异步补偿性 Worker 来进行业务回滚即可保证最终最一致性。

在发起分布式事务的业务模块的数据库里创建补偿性任务,基本上可以复用在其他存在分布式事务的场景里。如果不希望引入更加复杂的 TCC 和 Saga 框架,可以尝试利用此方式来解决架构微服务化之后带来的分布式事务的问题。


3.2 对数据库的依赖

  • 原则 1:主从库跨机房部署,测试环境也需主从复制,以防磁盘损坏带来巨大恢复成本。
  • 原则 2:SQL 尽量简单清晰,避免多层嵌套语句难以调试。
  • 原则 3:业务迭代快时,谨慎使用外键,防止隐藏级联风险与变更疏漏。
  • 原则 4:避免超长 varchar 存 JSON;并发更新时容易丢失修改,若需存复杂结构,可考虑拆表或并发锁。

3.3 对消息中间件的依赖

  • 原则 1:先写库/缓存,再发送消息,确保消费方反查时数据可见。
  • 原则 2:消息携带版本号或高精度时间戳,支持乱序消费时判断先后。
  • 原则 3:消息尽量全量,减少消费方反查接口带来的耦合与延迟。
  • 原则 4:标记字段变更标识,避免无效通知引发误执行。

4. 分布式事务实战案例

结合第 3.1 节所述,示例流程:

用户 订单服务 任务表 库存服务 异步Worker 下单请求 写初始化任务 调用扣减库存 标记已成功 下单成功 保留初始化或标记待回滚 下单失败 alt [接口成功 && 本地写单成功] [接口失败/超时 || 本地写单失败] 定时扫描任务表 查待回滚任务 异步返还库存 标记补偿完成 用户 订单服务 任务表 库存服务 异步Worker

5. 小结

我们围绕“怀疑下游”原则,介绍了针对 RPC 服务、数据库和消息中间件的治理要点;并通过“本地任务表+异步补偿”方案,演示了无需框架的分布式事务落地路径。 希望能提供一些帮助和思路。

在这里插入图片描述

http://www.xdnf.cn/news/5443.html

相关文章:

  • 利用“Flower”实现联邦机器学习的实战指南
  • html body 设置heigth 100%,body内元素设置margin-top出滚动条(margin 重叠问题)
  • 从零到精通:探索 GoFrame 框架的 SSE 优势与实战经验
  • 进程(沉淀中)
  • 运动员技术等级分为国际级运动健将
  • uniapp-商城-52-后台 商家信息(商家信息数据,云对象使用)
  • Java学习手册:服务注册与发现
  • 应急响应基础模拟靶机-security2
  • 咨询规划:精读53页信息化部门如何制定三年战略规划方案【附全文阅读】
  • 660先生与我——高等数学水平自测一、高等数学水平自测二
  • AugmentCode 非常昂贵的新定价
  • 第二十三节:图像金字塔- 图像金字塔应用 (图像融合)
  • 一个.Net开源的关系管理系统
  • 7系列 之 SelectIO 资源
  • Python Cookbook-7.10 在 MySQL 数据库中储存 BLOB
  • Linux网络基础 -- 局域网,广域网,网络协议,网络传输的基本流程,端口号,网络字节序
  • 弹性Reasoning!通过RL训练控制推理预算,提升模型的推理能力和效率!
  • 5000字总结 HTML5 中的音频和视频,关羽标签、属性、API 和最佳实践
  • 实战演练:用 AWS Lambda 和 API Gateway 构建你的第一个 Serverless API
  • uniapp-商城-53-后台 商家信息(更新修改和深浅copy)
  • 深度解析谷歌TPU架构:从硬件原理到AI模型优化实战
  • USB学习【7】传输模式
  • 【内网渗透】——MS14-068漏洞利用以及复现黄金票据
  • 操作系统: 第三章节 :中断和处理机调度
  • Scrapy 核心组件解析:Request Response 的深度应用与实战
  • 缓存(5):常见 缓存数据淘汰算法/缓存清空策略
  • Oracle Goldengate并行复制进程状态查看没有transaction信息
  • 不可导的几种情况
  • ARMV8 RK3399 u-boot TPL启动流程分析 --start.S
  • TypeScript 装饰器详解