《系统架构师成长之路》第 8 期|微服务:单体拆分的时机、粒度与通信
摘要/导读 :上一期我们聊了三种架构风格,很多同学听完就问:那到底要不要把单体拆成微服务?本期不讲"微服务万能",而是用工程视角回答三个最实际的问题------什么时候该拆 (时机)、拆到多细 (粒度)、拆完怎么通信(通信)。你会亲手算一遍"为什么同步链路越长越脆弱",理解 RPC 与消息两种方式的取舍。读完你就能对"要不要微服务"给出有理有据的判断。
一、先搞清楚:单体不是贬义词
很多新手以为"微服务 = 先进,单体 = 落后",这是最大的误区。单体(Monolith)指一个部署单元内含全部功能:订单、库存、用户全在一个进程里。
单体的优点恰恰是微服务的痛点:调用是本地函数调用(快、可靠、天然事务)、部署简单、调试直观。一个 5 人团队做 MVP,单体是最优解------拆成微服务只会增加运维负担。
微服务(Microservices)的本质是:把一个进程拆成多个独立部署、独立扩展、独立故障的小服务,每个服务拥有自己的数据。

图8-1 单体与微服务对比:左侧单体为一个进程内含订单/库存/用户等全部模块;右侧微服务按业务边界拆为多个独立服务,每个服务独立部署、独立数据库。
二、拆分的时机:四个"该拆"的信号
拆分的本质是用复杂度换独立性。什么时候这笔交易划算?出现以下四个信号之一,才考虑拆:
- 团队扩容失灵:新成员入职三个月,改一个需求仍然要动五个模块------单体的认知负荷超过了团队承载;
- 部署相互踩踏:A 团队改一行代码,要等 B 团队的代码一起发版------发布被"最长阻塞路径"绑架;
- 扩展方向不同:读多写少的服务与 CPU 密集的服务挤在一起,无法单独扩容(一个扩容全体扩容,浪费资源);
- 故障范围失控:一个慢查询拖垮整个进程,所有业务一起抖动。
推导:康威定律(Conway's Law) ------系统结构会镜像组织沟通结构:两个人分别负责"订单"与"库存",代码自然分成两块;10 个人共管一个代码库,沟通成本会让他们不自觉地在代码里"划地盘"。所以拆不拆,先看团队能不能各自闭环------这就是"拆分先看组织"的原因。
三、拆分的粒度:按"业务能力"切,不按"技术层"切
最常见的错误是按技术层拆:一个"数据库服务"、一个"前端服务"、一个"逻辑服务"。这样拆出来的"服务"没有独立业务价值,反而把每一层都变成分布式的(网络开销 ×3)。
正确的做法是按业务能力(Business Capability)拆,即领域驱动设计里的"限界上下文":订单域、库存域、用户域、支付域......每个域内的领域模型自洽,域与域之间通过明确的接口协作。
逐步推导服务边界:
- 第 1 步:画出业务流程(下单 → 扣库存 → 支付 → 出库);
- 第 2 步:圈出"每个步骤里会一起变化、一起理解"的业务概念(下单与订单状态一起变;库存与仓库位置一起变);
- 第 3 步:把"会一起变"的概念收进同一个服务,把"各自独立变化"的概念隔开------这就是"高内聚、低耦合"在服务划分上的落地;
- 第 4 步:给每个服务配独立数据库(关键!)------共享数据库是"伪微服务",因为数据库的锁与事务会把服务重新耦合回一体。
四、拆完怎么通信:RPC 与消息
服务之间不能再用"本地函数调用",只有两种主流方式:同步 RPC (HTTP/REST、gRPC)与异步消息(Kafka、RabbitMQ)。
4.1 同步 RPC:直观但会"串联放大故障"
下单要走"订单 → 库存 → 支付"的同步调用链。假设每个服务独立可用性都是 99%,整条链路成功的概率是多少?
逐步推导(乘法公式):一次完整请求要三个服务同时成功才成功。三个独立事件同时发生的概率 = 各自概率相乘:
- 1 个服务:P1=0.99=99.0%P_1 = 0.99 = 99.0\%P1=0.99=99.0%
- 2 个服务:P2=0.99×0.99=0.9801≈98.0%P_2 = 0.99 \times 0.99 = 0.9801 \approx 98.0\%P2=0.99×0.99=0.9801≈98.0%
- 3 个服务:P3=0.993=0.970299≈97.0%P_3 = 0.99^3 = 0.970299 \approx 97.0\%P3=0.993=0.970299≈97.0%
- 10 个服务:P10=0.9910≈0.904=90.4%P_{10} = 0.99^{10} \approx 0.904 = 90.4\%P10=0.9910≈0.904=90.4%
所用技巧:幂与乘法公式 ------"链路整体可用性 = 各环节可用性之积",这是概率论里独立事件乘法公式的直接应用。可见服务越多、链路越长,可用性指数级下跌:这就是为什么微服务必须配重试、超时、熔断------不是锦上添花,而是数学上的必然。

图8-2 同步链路可用性随服务数量递减(0.99ⁿ):横轴为链路中服务个数,纵轴为整体可用性;3 个服务约 97%,10 个服务约 90.4%,链路越长越脆弱。
4.2 异步消息:故障隔离,换来最终一致
用消息总线替代同步调用:订单服务发布"订单已创建"事件后立即返回,库存、支付各自订阅消费(上一期事件驱动已铺垫)。
对比同步链路:B 挂了,A 的请求直接失败;而消息模式下,B 挂了事件在总线里排队,B 恢复后继续消费------故障被隔离在单个消费者内 。代价是系统变成最终一致:下单成功那一刻,库存扣减可能还没发生,需要补偿/对账机制兜底。
4.3 怎么选:三个判别问题
| 判别问题 | 选 RPC | 选消息 |
|---|---|---|
| 调用方是否必须立刻知道结果? | 是(如余额查询) | 否(如下单后通知) |
| 失败后能否接受"稍后处理"? | 否 | 能 |
| 需要强事务一致吗? | 需要(尽量留在同一服务内) | 不需要(可最终一致) |

图8-3 同步 RPC 与异步消息对比:上图同步链路 A→B→C 串联,任一环故障整链失败;下图异步消息中 A 发布事件到总线,B/C 各自消费,单点故障被隔离。
五、防坑清单:拆了之后最常见的三个坑
- 共享数据库:两个服务读写同一张表 → 服务边界名存实亡。必须各管各库,跨库一致性用 Saga 模式。
- 分布式事务滥用:能在一个服务内完成的事务,绝不要拆出去用分布式事务"硬扛"------代价是性能与复杂度双重爆炸。
- 忘了治理 :没有服务注册发现、配置中心、链路追踪(如 Zipkin/SkyWalking),出问题时连"请求走过了哪些服务"都不知道------拆之前先把观测性建起来。
小结
- 单体不是落后,微服务不是先进------拆分的本质是用复杂度换独立性;
- 该拆的四个信号:团队扩容失灵、部署踩踏、扩展方向不同、故障范围失控;
- 粒度按业务能力/限界上下文切,每个服务独立数据库;
- 通信二选一:同步 RPC 直观但链路可用性指数衰减(0.99¹⁰≈90.4%),异步消息隔离故障但换来最终一致;
- 拆之前先建好治理与观测设施。
思考题:
- 你们当前系统出现"该拆"的哪个信号了?最不明显的那个信号是什么?
- 为什么"按技术层拆"(数据库服务/前端服务)在分布式下会事倍功半?
- 如果一个操作需要强一致(如转账),用消息队列实现会有什么隐患?
下期预告:第 9 期《分布式系统的基石:从 CAP 定理到一致性模型》,把"最终一致到底有多一致"这个问题彻底讲透。
参考资源:
- Martin Fowler《Microservices》导论:https://martinfowler.com/articles/microservices.html
- Sam Newman《Building Microservices》(拆分时机与粒度经典):https://samnewman.io/books/building_microservices_2nd_edition/
- microservices.io(服务拆分模式目录:Decompose by business capability):https://microservices.io/patterns/decomposition/decompose-by-business-capability.html
- Chris Richardson《微服务架构模式》(Saga 模式):https://microservices.io/patterns/data/saga.html
- Conway's Law 原文:https://en.wikipedia.org/wiki/Conway's_law
- 《Designing Data-Intensive Applications》第 8 章(分布式一致性):https://dataintensive.net/