关于”要不要上微服务”的讨论里,情绪往往盖过事实。一边是把单体等同于”落后”,另一边是把微服务嘲讽为”把网络当函数调”。抛开这些口水,工程上真正要回答的只有一个问题:当前系统的哪些痛点,是非拆不可的?如果列不出具体条目,拆分就是在用确定的复杂度去换一个想象中的收益。
单体被低估的优势
单体最直接的好处是简单。一个进程、一份配置、一条部署流水线,本地起一套环境就能跑通完整业务。函数调用是进程内的,参数和返回值经过编译器检查,调用链一眼可见,断点可以直接从入口跟到数据库事务。出了问题,一个日志文件通常就能把上下文串起来。
事务是单体的另一个杀手锏。下单扣库存、改余额、写流水,在一个数据库事务里要么全成功要么全回滚,语义清晰、心智负担低。换成跨服务调用,同样的流程要拆成多阶段,补偿逻辑写起来比业务逻辑还长。
团队协作在小规模下也是单体占优。十个人以内的团队改同一个代码库,只要模块边界划得不太离谱,配合 code review 和持续集成,迭代速度往往比拆成十几个服务更快。部署一次就上线,不存在”先发下游再发上游”的编排问题。
什么时候开始疼
单体的不适感通常是渐进出现的,有这么几个典型信号。
部署耦合是最容易察觉的。改一行文案要重新构建、重新回归整个应用;发布窗口越来越长,几个人同时改代码就会互相排队。如果团队里出现”周五不敢发版”的集体默契,说明发布粒度已经粗到影响节奏了。
团队规模是硬约束。当几十人上百人同时提交到一个仓库,合并冲突、测试耗时、相互踩踏会随人数非线性恶化。康威定律在这里不是格言而是现实:组织结构和系统结构迟早对齐,强行让多个团队共用一个部署单元,冲突会以各种形式冒出来。
扩展瓶颈是另一类信号。典型的形态是:应用里只有一小撮接口(比如报表导出、图片处理)吃掉了大半 CPU 和内存,其余接口都很轻。单体只能整体水平复制,等于为一小块热点买下整个应用的机器。数据库先撑不住也常见,写入集中在一个业务域,加缓存、加从库都救不回来时,说明这个域该有自己的数据边界了。
还有发布风险的感知变化。早期发版出问题,回滚就是重启一个进程;到了后期,一次发版牵动半个系统,回归范围无法穷尽,团队对发版的恐惧本身就是架构该调整的信号。
拆分的边界怎么划
决定要拆之后,最难的不是技术选型,而是”沿哪条线切”。切错了,跨服务调用会多到把分布式代价吃回来。
主流的划法是按业务能力切。订单、支付、库存、会员,各自对应相对独立的业务语义和变更节奏。判断标准可以问一句:这个模块的需求文档,是不是经常和另一个模块的需求写在一起?如果是,它们大概率该在一起;如果两拨产品经理各写各的、发版节奏完全不同,那才是天然的缝。
数据自治是另一条硬原则。一个服务应该拥有自己的数据存储,其他服务只能通过接口访问,不能直连别人的表。一旦允许跨服务 join 或共库,服务边界就名存实亡,后续任何一方改表结构都会波及对方。数据所有权划清了,拆分才算真正落地。
拆分粒度上宁粗勿细。两个服务之间如果每天要来回调几十次才能完成一次业务动作,那它们多半不该分开。服务数量翻倍,运维成本、链路排障成本、契约维护成本同步翻倍,而业务收益未必跟着涨。
拆完要付的账
分布式带来的代价是结构性的,躲不掉,只能正视。
事务语义首当其冲。跨服务的操作只能用最终一致性:本地事务加消息投递、TCC、Saga 补偿,各有适用场景,但没有一种能还原数据库事务的简单心智。补偿逻辑要处理”回滚也失败”的情况,测试用例成倍增加。
网络成为新的失败源。超时、重试、半开连接、慢查询拖垮线程池,这些在进程内调用里不存在的问题,拆分后全部进场。每个跨服务调用都要想清楚超时定多少、失败了降级还是报错、重试会不会打爆下游。
可观测性从”顺手”变成”刚需”。一次请求穿过五六个服务,没有统一的 trace id 和日志规范,排查一个问题要在好几个系统之间手工拼时间线。调用链追踪、指标监控、结构化日志,这些基础设施要在拆分前就位,而不是拆完再补。
契约管理也是长期成本。接口一旦对外发布,改动就是协作事件,版本兼容、灰度切流、废弃策略都要有章法。单体时代”改个函数签名顺手就改了”的日子一去不返。
演进路径:先模块化,再按需拆
更稳妥的走法是先在单体内部做模块化。按业务域把代码分成边界清晰的模块,模块之间只暴露接口、不共享数据表,用构建工具强制依赖方向,禁止跨模块直接访问内部实现。这一步不引入任何分布式复杂度,却把”边界感”先建立起来。
模块化跑顺之后,再挑真正的痛点模块拆出去。判断依据回到开头那几条:是不是独立扩展收益明显?是不是团队规模撑不住共仓?是不是发布节奏差异大?三者占其一,才值得动手;都不占,就让它继续待在单体里。
拆的时候优先拆”边缘”而非”核心”。把变更频繁、依赖较少的模块先独立成服务,跑通部署、监控、回滚的全套流程,团队积累了分布式运维经验之后,再考虑动交易链路这类核心域。反过来先拆核心,等于拿风险最高的部分练手。
单体和微服务不是非此即彼的信仰题,而是不同规模、不同阶段下的工程权衡。单体不是原罪,微服务也不是勋章,能把当前痛点解决掉、又不为未来埋雷的结构,就是好结构。
Comments (2)
看完了,最认同「先能跑再优化」这句。
这类文章应该多写,比官方文档好读。
Please Sign in to join the discussion.