原文作者:Martin Fowler | 发布时间:2018-04-24
原文地址:https://martinfowler.com/articles/break-monolith-into-microservices.html
前言:拆分什么、何时拆分
随着单体系统体量日趋庞大、维护成本激增,大量企业选择将其改造为微服务架构。这是一条值得投入但绝不轻松的落地之路。过往实践证明:稳妥的改造路径,要从边界简单的服务起步,优先抽取业务价值高、迭代频繁的垂直业务能力;拆分出的服务初期体量可以偏大,且尽量不再反向依赖遗留单体。每一轮迁移改造,都要让整体架构获得确定性优化(原子式演进)。
将单体系统改造为微服务生态是一场漫长工程。企业落地该改造的诉求通常包括:支撑业务规模化扩容、加速研发迭代效率、降低系统变更成本;依托微服务,企业可拆分多支团队并行交付、独立迭代;快速落地业务创新试验、摆脱单体改造成本高昂的桎梏。
改造过程中最核心的架构难题是:确定哪些业务能力要拆分、拆分时机、增量迁移方案。本文面向研发、架构师、技术管理者,提供一套落地方法论。
下文以多层架构在线零售系统为案例讲解,该系统前后端、业务逻辑、数据库深度耦合,是绝大多数企业单体项目的典型形态;选用它的原因是:技术栈成熟,适合渐进拆分而非全盘重写。
一、目标:最终的微服务生态蓝图
动工拆分前,团队必须对齐微服务生态的统一认知:微服务由若干服务组成,每个服务封装一项独立业务能力(Business Capability,即领域内为达成业务目标所需完成的业务动作)。
- 每个微服务对外暴露标准化API,可供开发者自助调用、发现接入;
- 服务拥有独立生命周期:可独立编码、测试、发布上线;
- 组织架构配套:长期稳定的自治团队全权负责一个或多个服务;
破除误区:微服务里的“微”不代表服务体量必须极小,服务规模取决于企业运维成熟度,Martin Fowler观点:微服务只是架构标签,不能用尺寸定义。
微服务生态带来的收益
- 独立服务加速业务创新试验;
- 专属自治团队长期维护对应服务;
- 各服务可按需选用差异化技术栈;
- 服务全生命周期独立管控;
- 通过服务组合快速落地业务创新;
- 拆分小型独立服务,降低开发者理解系统的认知负担;
- 支撑业务规模化扩张与团队扩容。
图1:服务封装业务能力,通过自助式API对外暴露数据与功能
二、落地分步指南
改造单体改微服务整体改造成本高昂、需要多轮迭代落地,动工前架构团队务必审慎评估:是否真的需要拆分、微服务是否适配自身业务。确认方向无误后,按下述步骤落地。
1. 热身起步:优先拆分耦合度低的边缘能力
落地微服务的前提是企业具备基础运维能力:按需开通部署环境、搭建支持服务独立构建/测试/发布的持续交付流水线、具备分布式架构的安全管控、问题排查、监控能力。无论新项目从零搭建,还是存量单体拆分,这套基建必不可少。近些年微服务配套基础设施飞速发展:服务网格(Service Mesh,专用微服务基础设施层,保障服务网络高速、可靠、安全)、容器编排、GoCD等CI/CD工具均已成熟可用。
落地建议:前两个拆分的服务同步完成基建、流水线、API网关建设;优先挑选和单体耦合极低、无需改动大量前端调用方、甚至无需自建数据库的业务能力拆分。
该阶段核心目标:验证交付流程、团队技能练兵、搭建基础平台,产出可独立部署、对外提供自助API的安全服务。
零售案例:第一个拆分:终端用户认证服务(单体调用该服务完成用户鉴权);第二个拆分:用户档案服务(为新客户端提供统一用户视图的门面服务)。
图2:从改动范围小的边缘能力起步,打磨整套运维落地能力,单体剥离鉴权逻辑,独立为鉴权服务
优先拆分边缘服务的原因:改造初期团队最大风险是不具备微服务运维能力,边缘服务用来练手补齐运维必备能力;等运维体系成熟后,再攻坚单体核心拆分难题。
2. 减少反向依赖:尽量避免新服务依赖旧单体
核心原则:新拆分出的微服务尽量不要反向依赖单体(数据、逻辑、API)。微服务的核心优势是独立快速发版,一旦依赖单体,就会被单体的发布节奏捆绑,丧失独立迭代能力。
改造的初衷本是摆脱单体变更成本高、迭代慢的痛点,因此拆分设计要朝着单体依赖新服务,而非新服务依赖单体的方向演进。单体调用新服务是理想依赖方向,不会拖累新服务迭代。
零售场景举例:下单(Buy)、营销活动(Promotions)是两大核心能力,下单结算时需要调用营销活动校验优惠。拆分顺序:先拆营销服务,再拆下单能力。拆分后下单逻辑仍留在单体,由单体主动调用独立后的营销服务,无反向依赖。
极端场景:新服务不得不反向调用单体
解决方案:单体新增对外API,新服务通过防腐层(Anti-Corruption Layer)调用单体接口;防腐层做数据模型转换,杜绝单体老旧业务概念污染新服务领域模型。API设计遵循标准领域定义,忽略单体内部杂乱实现。代价:改动单体、同步联调,新服务发布被单体发布绑定。
图3:优先拆分无反向依赖的服务,理想依赖:单体→新服务;劣等依赖:新服务防腐层→单体接口
3. 尽早拆分粘性耦合模块(强绑定公共能力)
完成边缘服务拆分、团队掌握微服务落地能力后,会遇到瓶颈:剩下的业务能力拆分后必然反向依赖单体,根源是单体中存在高度粘连、领域边界模糊、全系统多处依赖的公共模块(粘性能力)。
解决思路:定位该粘性模块,拆解为标准领域概念,再拆分成多个独立服务。
经典案例:Web会话(Session)
单体里的Session是数据杂糅容器:混杂用户偏好(收货/支付偏好)、浏览记录、商品收藏、页面访问轨迹等跨领域数据。不拆分会话,后续所有关联模块拆分都会被Session牵绊。
避坑:不要直接整体拉出一个Session服务,只会把进程内紧耦合变成跨网络紧耦合。
正确落地:增量拆解,逐个拆分:先拆用户收藏服务→再拆支付偏好服务,循序渐进。
图4:拆解高耦合公共会话,拆分为用户档案、收藏、支付配置等独立服务
工具建议:使用Structure101等代码结构分析工具,定位单体中耦合最重、制约整体拆分的粘性模块。
4. 垂直拆分+同步剥离数据存储(尽早拆分数据库)
拆分的终极目标是实现服务独立发布,该原则指导所有拆分决策。传统单体大多分层紧耦合、多模块共用一套数据库,很多团队拆分误区:只抽前后端门面服务,数据库继续共用。这种拆分只能快速优化前端迭代效率,但核心业务仍共用单体数据库,整体发布受制于最慢的单体,算不上真正的微服务(微服务核心特征:去中心化独立数据存储)。共享数据库是服务独立拆分的最大阻碍。
正确策略:垂直整段剥离业务+配套剥离自有数据库,全链路客户端切换至新服务API。多应用读写同一库是数据无法拆分的元凶,团队按需选用数据迁移方案;推荐参考Stripe四阶段增量迁移方案:业务不停机、应用逐步切库、从共用数据库逐步切到服务私有库。
图5:业务连同数据一起剥离为微服务,对外提供新接口,所有调用方切换接入新API
反模式警示:只拆前后端逻辑、不拆分配套数据库,本质仍是分布式单体。
5. 优先拆分业务核心、高频变更模块
单体拆分难度极高,Neal Ford曾用精密器官外科手术类比拆分:提取一项业务能力,要连带数据、业务逻辑、前端组件完整剥离,并切换调用链路。拆分要持续权衡改造成本与落地收益(提速迭代、支撑扩容)。
筛选标准:优先拆分业务价值高、频繁迭代、拖累整体研发速度的模块。可通过两点筛选:
- 统计代码提交记录,筛选历史高频改动代码;结合产品 roadmap,锁定未来持续迭代的模块;
- 对齐业务、产品负责人,筛选产品差异化核心能力。
零售案例:用户个性化推荐模块,持续迭代优化用户体验、产品频繁做A/B试验,是优质拆分目标。
图6:拆分对业务价值最高、改动最频繁的业务模块
工具建议:CodeScene代码提交分析工具,过滤构建脚本自动变更的无效提交,结合产品规划锁定待拆模块。
6. 按业务能力拆分,而非机械搬代码
单体拆分两条路线:①直接搬运现有代码抽成服务;②重新落地业务能力、下线单体旧代码。
多数人本能倾向第一种:依恋自己编写的代码(宜家效应:对自己付出劳动的产物高估价值、不愿舍弃),但该思路会拖累拆分进度。
- 方案1(复用抽代码):适合业务逻辑复杂、核心知识产权密集的模块(如定价&营销规则引擎),代码沉淀大量业务规则,重构成本远高于搬迁;
- 方案2(重写新服务,废弃老代码):适合简单CRUD模块(如用户档案),多为模板化增删改查,老旧框架、配置逻辑冗余,重写性价比更高。
行业结论:绝大多数场景推荐重新开发新服务、下线单体旧代码,复用老代码弊端:
- 老代码充斥老旧配置、缓存、存储等环境绑定的冗余模板代码,新微服务运行环境完全不同,几乎全量要改写;
- 老旧代码没有遵循标准领域模型,数据结构和新领域设计不符,重构工作量巨大;
- 长年迭代遗留高坏味道代码(代码毒性高),复用收益极低。
图7:高价值低冗余代码:抽取复用;低价值高坏味道代码:重新开发、旧代码下线
工具建议:CheckStyle等代码质量工具,评估代码毒性,决策重写还是搬迁。
7. 先粗粒度大服务,再逐步细化拆分(从宏观到微观)
DDD限界上下文是划分服务边界的有效手段,但大量团队走向另一个极端:单体直接拆成无数细碎CRUD小服务,形成贫血服务集群。弊端:无法独立发布、分布式事务泛滥、故障排查困难、远超团队运维承载能力。
落地原则:初期围绕完整领域拆粒度偏大的宏服务;等团队运维、多服务发布能力成熟后,再做二次细化拆分。微服务的“微”没有固定标准:以团队可独立运维、发布的服务数量上限为准。
零售案例:初期下单(Buy)服务统一收纳购物车+结算全逻辑;后续团队能力提升,再拆分为购物车服务、结算服务两个微服务。
图8:先粗粒度整合成大服务,后续运维成熟再细化拆分
技术选型:采用Richardson成熟度模型L3(超链接驱动API),通过资源链接实现未来无痛拆分,调用方无需提前感知内部拆分变化。
8. 原子式演进:分步落地,每一步都让架构变好
全盘推翻重写单体是伪命题,落地风险极高,很多拆分项目因预算耗尽、组织架构调整、业务方向变更中途搁浅。因此采用原子式演进迁移:单次拆分是不可分割的完整单元,要么全量落地、要么完整回滚;每一步改造后,系统架构必须向最终微服务目标靠近,不能越改越乱。(架构适配函数:每轮改造后架构指标向目标收敛)
鉴权服务落地举例(反面错误落地)
- 新建基于OAuth2的独立鉴权服务;
- 单体新增链路调用新鉴权服务;
项目中途暂停、转做其他功能。
结果:系统同时保留两套鉴权逻辑(原有账号密码+新OAuth),架构复杂度上升,背离提速迭代的改造初衷。
正确原子落地三步闭环(缺一不可)
- 拆分落地新服务;
- 全量切换所有调用方至新服务;
- 删除单体内部旧业务代码。
图9:原子化三步闭环演进,持续向微服务目标收敛
高频反模式:新服务只给新业务使用,老单体旧逻辑永久保留,两套逻辑长期并存。
出现该问题根源:团队只盯着短期收益、新项目排期挤压老代码下线工作量。
解决思路:切分更小的原子改造单元,缩短单轮改造周期,拆分项目可随时暂停、重启。
文末总结
单体拆微服务是长跑项目,依托原子化小步迭代、闭环下线旧代码,稳步蚕食老旧单体,平稳完成架构升级。
专业名词速查(便于落地查阅)
- Business Capability:业务能力
- Anti-Corruption Layer(ACL):防腐层
- Bounded Context:限界上下文(DDD)
- Service Mesh:服务网格
- Atomic Evolution:原子式演进
- Strangler Fig Pattern:绞杀者模式(渐进改造)
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接
你这主题排版得好,我的主题不行,主要是不懂代码,都是ai搞的。
文章评论