SpringNotes

第 15 章:微服务拆分

zjc 于 2026-01-15 发布

这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 微服务拆分不是把代码按目录切开,而是重新划分业务能力、数据所有权、团队协作和发布边界。拆得好,系统独立演进;拆得差,只是把单体复杂度变成分布式复杂度。

15.1 单体与微服务

单体:

Client -> Monolith -> One Database

优点:

  1. 开发简单;
  2. 事务一致;
  3. 调试方便;
  4. 部署少;
  5. 初期成本低。

痛点:

  1. 发布互相阻塞;
  2. 局部故障影响全局;
  3. 团队协作冲突;
  4. 技术栈统一约束;
  5. 数据库成为瓶颈。

微服务:

Client
  -> Gateway
     -> Order Service -> Order DB
     -> Product Service -> Product DB
     -> Inventory Service -> Inventory DB

微服务解决的是组织和演进问题,不是性能银弹。

15.2 拆分依据

常用方法:

方法 核心问题
DDD 限界上下文 业务语言和边界在哪里
数据所有权 谁拥有哪些表和事实
变更频率 哪些能力变化快
团队拓扑 团队如何协作
可伸缩性 哪些模块资源差异大
故障隔离 哪些能力需要独立降级

优先级:

业务边界 > 数据边界 > 团队边界 > 技术边界

不要按技术层拆成 Controller 服务、Service 服务、DAO 服务,这会放大远程调用。

15.3 限界上下文

电商示例:

Catalog Context:商品、类目、搜索词
Inventory Context:库存、预占、入库
Order Context:订单、状态、履约指令
Payment Context:支付单、渠道、退款
Logistics Context:运单、配送、轨迹

同一个词在不同上下文有不同含义:

词语 Catalog Order Logistics
Product 商品资料 下单时快照 包裹内容
User 浏览者 买家 收件人

跨上下文通信应使用明确契约和防腐层,而不是共享实体类。

15.4 服务粒度

过粗:

trade-service 包含订单、支付、库存、履约

过细:

order-query-service
order-command-service
order-validation-service
order-price-service

粒度判断:

信号 说明
两个服务总是同时发布 边界可能错
一个服务需要频繁查另一个数据库 数据边界可能错
一个接口需要 5 个以上服务协作 可能过细
小团队维护 20 个服务 成本过高
数据库表仍跨服务强关联 拆分不完整

建议从“粗粒度微服务”开始,边界稳定后再细化。

15.5 数据拆分

阶段演进:

阶段 1:单库单 schema
阶段 2:单实例多 schema
阶段 3:服务独立数据库
阶段 4:按业务进一步分库分表

原则:

  1. 每个服务拥有自己的数据;
  2. 不允许跨服务直连表;
  3. 跨服务查询用 API 或事件同步副本;
  4. 报表使用数仓或搜索索引;
  5. 共享只读主数据要明确所有者。

错误示例:

Order Service -> select * from inventory_db.stock

正确方向:

Order Service -> Inventory Service API
Order Service <- InventoryChangedEvent

15.6 服务通信

同步调用:

Order -> Inventory -> Product

优点:实时、简单。

问题:

  1. 级联失败;
  2. 延迟叠加;
  3. 可用性下降;
  4. 强耦合;
  5. 事务复杂。

异步事件:

Order -> OrderCreatedEvent -> Kafka
Inventory 消费事件
Notification 消费事件

优点:解耦、削峰、扩展消费者。

问题:

  1. 最终一致;
  2. 幂等复杂;
  3. 排查链路长;
  4. 事件治理要求高。

选择:

场景 建议
下单前检查库存 同步或预检
下单后扣库存 事件或 Saga
发送通知 事件
查询实时价格 同步缓存
生成报表 异步任务

15.7 分布式事务

下单流程:

1. 创建订单
2. 预占库存
3. 支付
4. 确认订单

失败处理:

阶段失败 补偿
库存预占失败 取消订单
支付失败 释放库存
支付成功但订单失败 退款
履约失败 人工或自动补偿

方案:

方案 说明
Saga 每步有对应补偿
TCC Try、Confirm、Cancel
Outbox 保证事件可靠
消息最终一致 适合通知类
XA 强一致,性能和运维成本高

15.8 团队与流程

康威定律:

系统架构往往反映组织沟通结构。

团队模式:

模式 特点
按层团队 前端组、后端组、DBA 组
按业务域团队 订单团队、商品团队
平台团队 提供网关、消息、监控

微服务要求:

  1. 服务有明确 Owner;
  2. 团队可独立开发、测试、发布;
  3. 接口变更走契约;
  4. on-call 责任清晰;
  5. 公共库治理有平台团队。

15.9 拆分实施路径

1. 梳理业务流程和通用语言
2. 画出限界上下文
3. 标记数据所有权
4. 识别高变更和高故障模块
5. 选择第一个独立服务
6. 建立契约和可观测性
7. 数据迁移
8. 双写或灰度切换
9. 回归验证
10. 清理旧代码

第一个服务建议选择:

  1. 边界清晰;
  2. 依赖较少;
  3. 变更频繁;
  4. 可独立验证;
  5. 故障影响可控。

不要先拆核心支付账务这类高风险能力。

15.10 拆分评估表

维度 问题
业务边界 是否有独立通用语言
数据边界 是否独立拥有表
接口契约 是否稳定
发布独立性 是否可独立上线
可伸缩性 是否有独立扩容需求
故障隔离 是否能独立降级
团队 ownership 是否有维护者
观测能力 trace/log/metric 是否完整
回滚方案 能否快速回滚
成本 是否值得

本章小结

微服务拆分应以业务限界上下文和数据所有权为核心,从粗粒度服务开始,逐步建立独立数据库、契约、发布流程和观测体系。拆分不是越多越好,每增加一个服务都带来网络、事务、安全和排障成本。先证明边界稳定,再推进拆分。

思考题

  1. 为什么不能按 Controller、Service、DAO 拆微服务?
  2. 如何判断服务粒度过细?
  3. 跨服务查询有哪些替代方案?
  4. Saga 和 TCC 分别适合什么场景?
  5. 如何选择第一个拆分出来的服务?