PostsMapsLinks
架构设计

单体拆分时机

四种不同步与分布式税、四信号量化与黄红灯阈值、最疼优先与绞杀者渐进、逆康威操作、故障形态从全挂到级联、共享表收编顺序、Prime Video 拆错对象

拆分临界点看四种不同步,不看代码体积

模块化单体把边界立进代码之后,「核心稳了」常被误读成「可以物理拆了」,这是两回事。物理拆分先欠一笔 固定成本:分布式税(distributed tax),网络调用、分布式事务、多套流水线、跨进程排障,这些开销每天都要 交,与业务有没有受益无关;继续用模块化单体,这笔税是零,边界已经在代码里。收益端,物理拆分只有四种 货币化收益,各自对应一种不同步:团队不同步,两支以上团队抢同一个发布窗口;发布不同步,订单组要发 必须等库存组的回归跑完;流量不同步,大促时只有结算接口 CPU 打满,空闲模块也得跟着扩容;演进不同步, 营销一周三期活动页而风控一个月才敢动规则。任何一种不同步都不存在时,拆分是纯交税。反直觉的是体积 不构成判据:二十万行但一个团队、一次发布、流量均匀,可以继续单体;两万行但三支团队、四种节奏、一处 热点,已经该认真评估。体积是结果,不同步才是原因,别问系统有多大,问四件事还齐不齐步。

见:When to Split a Monolith: The Signals That Actually Justify It

拆分信号的红灯判据:撤不回与持续四个周期

把四条信号写成能看的数:一个发布单元背后的独立对齐团队数;过去 30 天里因别人的提交导致自己回滚的 次数;最热接口 CPU 占比以及整站扩容次数是否与它绑定;两个模块的发布频率是否差到一个数量级。数字进 同一张表每周看,一项越线是黄灯,先加模块边界和架构测试;两项以上越线且持续四个发布周期是红灯,再 立案评估。持续性是防伪关键:大促是一次脉冲,临界点是趋势,单次峰值不构成拆分理由。三个易误判处需要 分清。部署信号看的不是「发得慢」,慢可以靠按模块切测试、并行流水线缓解,回滚无法按模块撤回、误伤 无关功能才是越线,判据是失败无法单独撤回。流量信号先确认是计算热点还是数据库热点,数据库先到顶时 拆服务无效,该做读写分离或分片。演进信号不是问「谁想自己说了算」,而是错过对方的发布节奏会不会 直接丢钱或违规,合同、监管或收入的硬约束才算红。

见:Monolith to Microservices

红灯亮了一次只拆最疼的单元

就算红灯亮了,也不按领域名词把清单拆完。先拆同时命中团队独立、发布冲突、流量热点的那一个单元, 其余模块继续留在模块化单体里。最常见的失败是拆了六个、其中五个没有独立演进需求,那五个变成分布式 的大泥球,比原来更难改。拆分清单的第一行只能有一个名字,跑完两个发布周期、数字回绿,再考虑第二行。 落地手法是绞杀者模式(Strangler Fig):新服务像藤蔓包住古树一样逐步包住旧单体,入口路由把流量按功能 切给新服务,旧模块原地不动直到被完全替代。架构演进是连续搬迁,不是一次搬家晚会。

见:StranglerFigApplication

拆组织先于拆代码:逆康威操作

一支 12 人团队想拆出订单服务,惯例是先派两人开新仓库写服务,但康威定律复印的是现有沟通结构:这两个人 每天仍要找其余十人对齐字段口径,半年后订单服务变成需要全家会签的「共享库」,只是多了一次网络跳。 逆康威操作(Inverse Conway Maneuver)把方向倒过来:想拆出独立服务,先成立独立团队并明确它不参与原单体 的值班;团队边界稳定后,服务边界沿着团队边界自己长出来。Team Topologies 把这套组织设计工程化:价值流、 平台、赋能、复杂子系统四类团队配三种交互模式,先度量认知负载(cognitive load)再画边界。拿到拆分提案 先问拆完谁拥有它、它还依赖谁对齐,组织答案不出来,代码不动。

见:Team Topologies Key Concepts

拆分换掉故障形态:从全挂到级联慢

部署撤回只覆盖一半的隔离账,同进程还藏着运行时的账:任何模块的内存泄漏、Full GC、线程池打满都会拖垮 全进程,故障是全有全无,症状统一但同归于尽,这也是把故障隔离列为第五条拆分信号的原因。拆分后故障 变成级联失败(cascading failure):下单服务变慢,购物车等它超时,前端等购物车超时,慢像波浪往上游传, 每个环节的超时配置把局部慢放大成全局瘫。拆分没有消灭故障,而是把全挂换成部分慢:诊断从看一个进程的 日志变成跨服务追链路,还必须补上超时预算、熔断、舱壁隔离三件套,缺一件,可用性反而低于拆分前。 评估拆分收益时,故障隔离可以列为信号五,新故障模式的防御成本则计入分布式税。

见:Release It! Second Edition

先收共享表再拆进程

团队把订单模块拆成独立服务,但库存和订单仍共用一张库存表、两个服务各自直连改数据,此时 schema 变更 从编译期错误降级成运行时事故:原来字段改名编译器立刻报警,拆分后订单侧改字段、库存侧的 SQL 半夜挂掉, 外加两个服务各自缓存,同一行数据出现三个版本的真相。正确顺序是反的:先把共享表按归属收掉,订单模块 不再直读库存表、改走模块内接口,进程内也要走接口;等模块间所有数据访问都过了接口,把接口换成 HTTP 只是换传输方式。跳过这步拆进程,得到的是分布式大泥球:服务边界画在架构图上,真实耦合还埋在共享表里。 这与模块化单体的数据闸是同一件事的两段:先立表所有权,再动进程。

见:Database per Service

Prime Video 回单体的精确读法:拆错对象

2023 年 5 月,Prime Video 的音视频质量监控服务上了技术新闻:团队此前把它拆成 Step Functions 编排的多个 Lambda,高并发时发现架构本身跟不上直播流,五个组件每两秒产生几十 GB 中间数据,全靠 S3 存储来回倒手, 编排层的读写延迟成了瓶颈。修复不是加机器,而是把五个组件收回同一个进程、数据改走内存传递,成本降 90%。这个案例常被引用为「微服务失败」,更精确的读法是拆错对象:五个组件同属一条数据流水线、同一支 团队、同一发布节奏,四种拆分信号一个都不满足,它是「信号不齐就拆会怎样」的反向验证实例,该团队其他 服务至今仍用微服务。社区对该案例的批评也成立:数据密集型批处理是非典型场景,不能推广为微服务普遍 失败的证据,引用时标注这个边界。

见:Return of the Monolith

Copyright © 2024 Lionad - CC-BY-NC-CD-4.0