这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 微服务拆分不是把代码按目录切开,而是重新划分业务能力、数据所有权、团队协作和发布边界。拆得好,系统独立演进;拆得差,只是把单体复杂度变成分布式复杂度。
15.1 单体与微服务
单体:
Client -> Monolith -> One Database
优点:
- 开发简单;
- 事务一致;
- 调试方便;
- 部署少;
- 初期成本低。
痛点:
- 发布互相阻塞;
- 局部故障影响全局;
- 团队协作冲突;
- 技术栈统一约束;
- 数据库成为瓶颈。
微服务:
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:按业务进一步分库分表
原则:
- 每个服务拥有自己的数据;
- 不允许跨服务直连表;
- 跨服务查询用 API 或事件同步副本;
- 报表使用数仓或搜索索引;
- 共享只读主数据要明确所有者。
错误示例:
Order Service -> select * from inventory_db.stock
正确方向:
Order Service -> Inventory Service API
Order Service <- InventoryChangedEvent
15.6 服务通信
同步调用:
Order -> Inventory -> Product
优点:实时、简单。
问题:
- 级联失败;
- 延迟叠加;
- 可用性下降;
- 强耦合;
- 事务复杂。
异步事件:
Order -> OrderCreatedEvent -> Kafka
Inventory 消费事件
Notification 消费事件
优点:解耦、削峰、扩展消费者。
问题:
- 最终一致;
- 幂等复杂;
- 排查链路长;
- 事件治理要求高。
选择:
| 场景 | 建议 |
|---|---|
| 下单前检查库存 | 同步或预检 |
| 下单后扣库存 | 事件或 Saga |
| 发送通知 | 事件 |
| 查询实时价格 | 同步缓存 |
| 生成报表 | 异步任务 |
15.7 分布式事务
下单流程:
1. 创建订单
2. 预占库存
3. 支付
4. 确认订单
失败处理:
| 阶段失败 | 补偿 |
|---|---|
| 库存预占失败 | 取消订单 |
| 支付失败 | 释放库存 |
| 支付成功但订单失败 | 退款 |
| 履约失败 | 人工或自动补偿 |
方案:
| 方案 | 说明 |
|---|---|
| Saga | 每步有对应补偿 |
| TCC | Try、Confirm、Cancel |
| Outbox | 保证事件可靠 |
| 消息最终一致 | 适合通知类 |
| XA | 强一致,性能和运维成本高 |
15.8 团队与流程
康威定律:
系统架构往往反映组织沟通结构。
团队模式:
| 模式 | 特点 |
|---|---|
| 按层团队 | 前端组、后端组、DBA 组 |
| 按业务域团队 | 订单团队、商品团队 |
| 平台团队 | 提供网关、消息、监控 |
微服务要求:
- 服务有明确 Owner;
- 团队可独立开发、测试、发布;
- 接口变更走契约;
- on-call 责任清晰;
- 公共库治理有平台团队。
15.9 拆分实施路径
1. 梳理业务流程和通用语言
2. 画出限界上下文
3. 标记数据所有权
4. 识别高变更和高故障模块
5. 选择第一个独立服务
6. 建立契约和可观测性
7. 数据迁移
8. 双写或灰度切换
9. 回归验证
10. 清理旧代码
第一个服务建议选择:
- 边界清晰;
- 依赖较少;
- 变更频繁;
- 可独立验证;
- 故障影响可控。
不要先拆核心支付账务这类高风险能力。
15.10 拆分评估表
| 维度 | 问题 |
|---|---|
| 业务边界 | 是否有独立通用语言 |
| 数据边界 | 是否独立拥有表 |
| 接口契约 | 是否稳定 |
| 发布独立性 | 是否可独立上线 |
| 可伸缩性 | 是否有独立扩容需求 |
| 故障隔离 | 是否能独立降级 |
| 团队 ownership | 是否有维护者 |
| 观测能力 | trace/log/metric 是否完整 |
| 回滚方案 | 能否快速回滚 |
| 成本 | 是否值得 |
本章小结
微服务拆分应以业务限界上下文和数据所有权为核心,从粗粒度服务开始,逐步建立独立数据库、契约、发布流程和观测体系。拆分不是越多越好,每增加一个服务都带来网络、事务、安全和排障成本。先证明边界稳定,再推进拆分。
思考题
- 为什么不能按 Controller、Service、DAO 拆微服务?
- 如何判断服务粒度过细?
- 跨服务查询有哪些替代方案?
- Saga 和 TCC 分别适合什么场景?
- 如何选择第一个拆分出来的服务?