《系统架构师成长之路》第 8 期|微服务:单体拆分的时机、粒度与通信

《系统架构师成长之路》第 8 期|微服务:单体拆分的时机、粒度与通信

摘要/导读 :上一期我们聊了三种架构风格,很多同学听完就问:那到底要不要把单体拆成微服务?本期不讲"微服务万能",而是用工程视角回答三个最实际的问题------什么时候该拆 (时机)、拆到多细 (粒度)、拆完怎么通信(通信)。你会亲手算一遍"为什么同步链路越长越脆弱",理解 RPC 与消息两种方式的取舍。读完你就能对"要不要微服务"给出有理有据的判断。

一、先搞清楚:单体不是贬义词

很多新手以为"微服务 = 先进,单体 = 落后",这是最大的误区。单体(Monolith)指一个部署单元内含全部功能:订单、库存、用户全在一个进程里。

单体的优点恰恰是微服务的痛点:调用是本地函数调用(快、可靠、天然事务)、部署简单、调试直观。一个 5 人团队做 MVP,单体是最优解------拆成微服务只会增加运维负担。

微服务(Microservices)的本质是:把一个进程拆成多个独立部署、独立扩展、独立故障的小服务,每个服务拥有自己的数据。

图8-1 单体与微服务对比:左侧单体为一个进程内含订单/库存/用户等全部模块;右侧微服务按业务边界拆为多个独立服务,每个服务独立部署、独立数据库。

二、拆分的时机:四个"该拆"的信号

拆分的本质是用复杂度换独立性。什么时候这笔交易划算?出现以下四个信号之一,才考虑拆:

  1. 团队扩容失灵:新成员入职三个月,改一个需求仍然要动五个模块------单体的认知负荷超过了团队承载;
  2. 部署相互踩踏:A 团队改一行代码,要等 B 团队的代码一起发版------发布被"最长阻塞路径"绑架;
  3. 扩展方向不同:读多写少的服务与 CPU 密集的服务挤在一起,无法单独扩容(一个扩容全体扩容,浪费资源);
  4. 故障范围失控:一个慢查询拖垮整个进程,所有业务一起抖动。

推导:康威定律(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 各自消费,单点故障被隔离。

五、防坑清单:拆了之后最常见的三个坑

  1. 共享数据库:两个服务读写同一张表 → 服务边界名存实亡。必须各管各库,跨库一致性用 Saga 模式。
  2. 分布式事务滥用:能在一个服务内完成的事务,绝不要拆出去用分布式事务"硬扛"------代价是性能与复杂度双重爆炸。
  3. 忘了治理 :没有服务注册发现、配置中心、链路追踪(如 Zipkin/SkyWalking),出问题时连"请求走过了哪些服务"都不知道------拆之前先把观测性建起来。

小结

  • 单体不是落后,微服务不是先进------拆分的本质是用复杂度换独立性;
  • 该拆的四个信号:团队扩容失灵、部署踩踏、扩展方向不同、故障范围失控;
  • 粒度按业务能力/限界上下文切,每个服务独立数据库;
  • 通信二选一:同步 RPC 直观但链路可用性指数衰减(0.99¹⁰≈90.4%),异步消息隔离故障但换来最终一致;
  • 拆之前先建好治理与观测设施。

思考题:

  1. 你们当前系统出现"该拆"的哪个信号了?最不明显的那个信号是什么?
  2. 为什么"按技术层拆"(数据库服务/前端服务)在分布式下会事倍功半?
  3. 如果一个操作需要强一致(如转账),用消息队列实现会有什么隐患?

下期预告:第 9 期《分布式系统的基石:从 CAP 定理到一致性模型》,把"最终一致到底有多一致"这个问题彻底讲透。

参考资源:

相关推荐
Song_RsHt2 小时前
Redis 与微服务,什么场景下无可替代
java·redis·后端·学习·微服务
Aman19843 小时前
优雅的红点系统:MV数据视图分离与全局集中管理
系统架构·游戏程序
Figo_Cheung3 小时前
Figo潜空间层级认知理论:基于齐夫拓扑与黎曼几何的结构化模型研究
人工智能·架构·空间计算
洞见前行4 小时前
用 Tinker 热更新反向实现插件化:两个独立 APK 合二为一的原理详解
架构
这个DBA有点耶4 小时前
分布式集群的“最后一公里”:分片集群与透明分布式集群的工程代价拆解
数据库·分布式·架构
another heaven6 小时前
【C/C++ C++ 面向对象架构的五大特点与能力场景:封装、继承、多态、抽象与 RAII(含实战案例)】
c语言·c++·架构
9624567 小时前
餐饮 SaaS 优惠券系统架构演进(十三)券模板、发放批次与用户券数据模型:不可变快照、版本演进、状态归档与冷热分层
系统架构
微订校园外卖平台7 小时前
乡镇外卖跑腿代办系统怎么选:四端架构与实施核对
架构·外卖系统·本地生活·微订
这个DBA有点耶7 小时前
测试全绿,上线三周数据没入账——那个95%的兼容度报告,漏掉的是边界
数据库·mysql·架构