从微服务到模块化单体:架构师的两难抉择
我还记得那个决定“上微服务”的会议。
那是2016年。康威定律(Conway’s Law)被奉为圣经,Netflix 是我们的北极星。每场技术大会都在承诺:微服务能解决我们的扩展难题、团队协作问题和部署瓶颈。
“我们会拥有自治团队,”我们说,“独立部署、技术自由、真正的可扩展性。”
我们深信不疑。于是,我们把单体应用拆成了47个服务。
三年后,我们花在排查分布式链路追踪上的时间,比写新功能还多。“独立”的服务通过共享数据库和同步调用紧紧耦合在一起。每次部署都要协调五个团队。可观测性(observability)的账单眼看就要突破六位数。
我们只是用一套更昂贵的问题,换掉了原来那套。
如今,七年过去了,我看到了一个趋势:那些当初一窝蜂上微服务的团队,正在回撤——不是回到老式的“意大利面式”单体,而是转向一种更理性的架构:模块化单体(Modular Monolith)。
这并不是倒退,而是一种进化。
我们曾经相信的承诺
先说清楚一点:微服务本身没有错。它要解决的问题是真实存在的。
2010年代初,单体应用确实碰到了天花板:上百人的团队互相踩脚、部署排队长达数天、一个团队的bug能让整个系统崩溃、想单独扩展认证服务却不得不把整个单体一起扩容……
微服务描绘了一幅美好蓝图:
✓ 独立扩展 —— 只扩真正需要的部分
✓ 团队自治 —— 端到端负责自己的服务
✓ 技术灵活 —— 为不同任务选合适的工具
✓ 故障隔离 —— 一个服务挂了,其他还能跑
✓ 快速部署 —— 各自发布,高频交付
对亚马逊、Netflix、Uber 这样的巨头来说,这套方案确实奏效。他们有规模、有工程成熟度、有基础设施投入,也有匹配的组织结构。
但我们忽略了一个关键点:他们的规模不是我们的规模,他们的问题也不是我们的问题。
可我们还是照搬了他们的解决方案。
我们后来面对的现实
大约在微服务落地18个月后,问题开始浮现。
复杂性税(The Complexity Tax)
从前我们只监控一个应用,现在要管47个服务。每个服务都有自己的:
- 部署流水线
- 数据库(或者共用数据库,那就违背初衷了)
- 日志配置
- 指标看板
- 健康检查
- 熔断器
- 重试策略
- 超时设置
运维开销爆炸式增长。我们不得不引入:
- 服务网格(我们选了 Istio,结果它自己成了新难题)
- 分布式追踪(先用 Jaeger,后来换成 Datadog)
- 集中日志(ELK → Splunk)
- API 网关
- 服务发现
- 配置管理
- 密钥管理
我们只是把应用复杂度换成了基础设施复杂度——而后者更难理解和调试。
分布式调试噩梦
有用户反馈结账失败。简单吧?查日志就行。
但这次请求穿过了12个服务:
API网关 → 认证服务 → 用户服务 → 购物车服务 → 库存服务 → 定价服务 → 优惠服务 → 支付服务 → 订单服务 → 通知服务 → 分析服务 → 物流服务
到底哪里出错了?是超时?网络分区?数据库死锁?还是接口契约变了?
过去看个堆栈就能定位的问题,现在变成了一场“考古挖掘”。
逃不掉的耦合
我们按“限界上下文”设计服务,读过《领域驱动设计》,画过漂亮的架构图。
但现实很骨感。
支付服务需要用户数据,订单服务依赖库存,通知服务几乎要所有信息。
于是我们建起了同步调用。服务网格缓解了一些问题,但没解决根本:运行时依赖让服务逻辑上依然紧耦合。
定价服务一挂,结账就失败;库存服务变慢,首页也卡顿;认证服务一重启,30多个服务就得处理瞬时故障。
我们实现了物理分离,却没能摆脱逻辑耦合。
团队碎片化
康威定律是把双刃剑。
我们按服务划分团队:购物车团队管购物车服务,订单团队管订单服务。
但业务功能呢?它们横跨多个服务。
“加个礼品包装选项”要协调四个团队;“上线会员积分”涉及九个服务。简单功能变成了跨团队项目,充满依赖、会议和集成测试噩梦。
我们为了服务自治,牺牲了功能交付速度。
行业的集体反思
我们不是孤例。2019–2020 年左右,风向开始转变。
亚马逊 Prime Video:回归单体
2023年,Prime Video 工程团队发表案例:他们把音视频监控服务从微服务改回单体,成本直降90%。
原来的微服务架构包括:
- 用 AWS Step Functions 编排分布式组件
- 服务间通过 S3 传递数据
- 网络开销大
- 状态管理昂贵
合并成单进程后,他们:
- 消除了网络跳转
- 去掉了序列化开销
- 简化了可观测性
- 改用垂直扩展而非水平扩展
网上炸锅了:“亚马逊放弃微服务了!”
不,他们只是为具体问题选择了合适的架构。
Shopify:回归“模块化单体”
Shopify 在2022年的博客中坦言:他们拆过分服务,遇到类似问题,最终又合并回所谓的“模块化单体”。
关键洞察:“我们承受了微服务的复杂性,却没享受到它的好处。”
他们的服务其实是:
- 一起部署(共享发布周期)
- 通过直接调用耦合
- 规模小到不需要独立扩展
于是他们合并了代码,但保留了内部模块边界:清晰的接口、合理的数据库隔离。
Uber:混合路线
Uber 从未全盘微服务化。他们采取混合策略:
- 核心服务(调度、定价、支付)保持独立——这些确实需要独立扩展
- 小功能服务合并成“领域单体”
- 即使在大服务内部,也通过明确接口维持模块化
教训是:架构不是非黑即白,而是因地制宜。
什么是模块化单体?
先澄清概念:模块化单体 ≠ 老式“意大利面”单体。
老式单体:
- 依赖混乱
- 全局共享状态
- 难以理解
- 部署风险高
- 边界模糊
模块化单体:
- 单一部署单元
- 模块边界清晰
- 模块间通过定义好的接口通信
- 理论上可拆出独立服务
- 共享进程,但逻辑隔离
打个比方:
-
微服务 = 一片独栋住宅区
- 每栋房子独立
- 自己维护水电
- 基础设施开销大
- 通信靠“马路”(网络)
- 适合需求差异大的场景
-
模块化单体 = 一栋公寓楼
- 共享水电管道
- 户与户有墙隔离
- 成本低、管理简单
- 通信靠“门”(函数调用)
- 适合需求相似的场景
如果你住公寓就够用,何必非买独栋?
架构师的决策框架
那么,到底该怎么选?这是我现在的判断标准。
什么时候适合微服务?
-
真有扩展需求
系统每秒处理百万级请求,且不同模块的负载差异巨大。
(不是“将来可能要扩展”,而是现在就有明确瓶颈) -
团队规模大
50+工程师在同一领域工作,共享代码库导致严重协作冲突。
(不是“想要自治”,而是已有组织痛点) -
技术栈确实不同
某部分必须用 Python(比如机器学习),另一部分必须用 Go(高性能 API)。
(不是“想试试 Rust”,而是有真实技术需求) -
业务域真正独立
支付系统和库存系统互不影响,一个挂了不会拖垮另一个。
(现实中很少见——大多数业务比我们想象的更紧密) -
DevOps 成熟
你有:- 每个服务的 CI/CD
- 完整的可观测体系
- 能处理分布式调试的 on-call 团队
- 熟悉分布式系统的工程师
如果连基础监控都搞不定,加30个服务只会雪上加霜。
什么时候适合模块化单体?
-
早期阶段 / MVP
产品还在验证市场,业务边界模糊,可能三个月就大改方向。
→ 先做简单架构,等需求稳定再拆。 -
中小团队(5–20人)
团队能高效协作,理解整个代码库。
→ 分布式系统的开销远大于自治收益。 -
读多写少的系统
大部分是查询操作,靠缓存和数据库副本就能扩展。
→ 计算层无需独立伸缩。 -
业务逻辑高度交织
订单依赖库存,库存依赖定价,定价又依赖用户……
→ 强行拆分会制造更多耦合。 -
成本敏感
运行40个服务的成本远高于4个:- 每个服务都要计算资源(常被过度分配)
- 负载均衡器
- 日志监控
- 运维人力
→ 合并能显著降本。
如何从微服务迁回模块化单体?
如果你已在微服务泥潭中挣扎,这里是一条务实路径:
第一步:识别服务集群
画出你的服务拓扑图:
- 哪些服务总是一起部署?
- 哪些重度共享数据?
- 哪些流量低到不值得独立部署?
→ 这些就是合并候选。
第二步:从低风险服务开始
别第一天就把支付和认证合并!
先从内部工具、边缘功能入手,控制爆炸半径。
第三步:保持模块边界
合并不等于乱炖。即使在一个代码库里,也要:
- 用不同包/命名空间隔离
- 定义清晰接口
- 禁止直接访问其他模块数据库(走服务层)
- 明确模块负责人
→ 目标:未来仍可按需拆出。
第四步:全面度量
跟踪关键指标:
- 部署频率(应提升)
- 故障排查时间(应缩短)
- 开发效率(应感觉更快)
- 运维成本(应下降)
→ 如果没改善,说明方法错了。
第五步:持续迭代
这不是一次性工程。也许你把10个服务合并成3个模块化单体,同时保留5个真正独立的服务。
没有“最多服务”或“最少服务”的奖杯,只有“有效解决问题”的胜利。
真实的权衡
坦白说,两种架构各有得失:
微服务的优势:
✓ 真正独立扩展
✓ 技术栈灵活(当真需要时)
✓ 故障隔离(若设计得当)
✓ 团队自治(需成熟组织支撑)
微服务的代价:
✗ 运维复杂度高
✗ 分布式调试困难
✗ 网络延迟
✗ 数据一致性挑战
✗ 基础设施成本高
模块化单体的优势:
✓ 运维简单
✓ 调试容易
✓ 成本低
✓ 开发效率通常更高
✓ 模块间支持原子事务
模块化单体的代价:
✗ 需要纪律维持边界
✗ 部署是“全有或全无”(可用功能开关缓解)
✗ 垂直扩展有上限(但通常比你想的高)
✗ 共享故障域
没有绝对优劣,只有是否适合当下。
架构成熟度模型
15年经验告诉我:架构随组织成熟度演进。
-
阶段1:小团队(5–15人)
→ 单体(最好从第一天就模块化)
→ 简单基础设施
→ 快速迭代,清晰归属 -
阶段2:成长团队(15–50人)
→ 模块化单体 + 1–2个独立服务(如文件存储、AI模型)
→ 加强自动化测试和部署 -
阶段3:大团队(50–200人)
→ 模块化单体 + 微服务混合
→ 仅在真正需要独立性的领域拆分
→ 建立 DevOps 文化和平台团队 -
阶段4:超大组织(200+人)
→ 以微服务为主,但严格治理
→ 平台工程成为核心能力
→ 但仍会在合适场景用模块化单体
你不能跳过阶段。10人创业公司搞微服务,只是在 cosplay Netflix。
如果重来一次,我会怎么做?
如果能回到2016年,我会:
1. 从模块化开始
第一天就构建有清晰边界的单体:
- 按领域分包
- 模块间接口明确
- 数据库逻辑隔离(即使共用实例)
- 用 CI 检查禁止跨模块违规依赖
→ 这样未来才有选择权。
2. 战略性拆分
不是“全部拆”,而是:“这个模块确实有不同扩展/技术/团队需求。”
一次拆一个,测量效果,再决定下一步。
3. 打好基础
无论单体还是微服务,都必须有:
- 完善的测试(单元、集成、端到端)
- 自动化部署
- 可观测性(日志、指标、链路)
- 功能开关(安全发布)
→ 这些比服务数量重要得多。
4. 为变化而设计
别只为今天的组织或规模优化。
要设计成:接口清晰、耦合低、内聚高、易测试。
→ 这些原则超越架构风格。
架构师的真正职责
残酷的真相是:
你的工作不是选微服务还是单体,而是理解上下文并做出明智权衡。
每个架构决策都有代价。你的任务是:
- 明确你真正要解决的问题
- 了解你的约束条件(团队、预算、时间、规模)
- 诚实评估选项(而非追逐潮流)
- 做出适合当前情境的决定
- 规划演进路径(因为情境会变)
最好的架构师,不是画出最漂亮架构图的人,而是帮团队可持续交付价值的人。
如何判断架构是否成功?
别数服务数量。看这些:
✅ 开发者能独立交付功能
(不是“理论上能”,而是实际上能)
✅ 调试问题在合理时间内完成
(出事了能快速定位修复)
✅ 新人能快速上手
(系统可理解、文档齐全、边界清晰)
✅ 成本与业务价值匹配
(没在基础设施上花冤枉钱)
✅ 团队不被运维压垮
(on-call 可控、部署顺畅、监控有用)
✅ 能持续演进而非推倒重来
(架构支持变化,而非阻碍它)
如果你满足这些,你的架构就是成功的——不管叫微服务、单体还是混合体。
钟摆还会再摆回来
我预测:
行业会过度纠正,大量团队因微服务之苦而回归单体。博客会宣称“微服务是个错误”。
然后,5–7年后,当这些单体再次膨胀、团队继续扩张,大家又会重新发现服务拆分的价值。钟摆将再次摆向微服务。
这很正常。技术趋势本就会震荡。每次摆动都让我们学到新东西。
真正的教训不是“微服务坏”或“单体好”,而是:上下文比教条更重要。
我对初级架构师的建议
当有人问我“该不该用微服务?”,我会反问:
“你具体想解决什么问题?”
不是“哪种架构酷?”
不是“Netflix 用什么?”
而是:你现在面临什么可衡量的具体问题,微服务能解决它吗?
如果答案是“以后可能要扩展”或“我们想要团队自治”(但你们只有8个人),那答案就是不。
如果答案是“我们每秒处理1000万请求,数据库扛不住了”或“80人团队部署要三天”,那或许可以考虑。
从简单开始,有目的地演进,解决真实问题,而非假设问题。
我们真正需要的架构
这些年我明白了一件事:
最好的架构,不是最复杂的,而是:
- 解决你真实的问题
- 匹配你团队的能力
- 支撑你业务的需求
- 能随情境变化而演进
- 不天天跟你作对
有时候是微服务,有时候是模块化单体,很多时候是混合体。
简单不是耻辱,复杂也没有奖赏。
作为架构师,你的目标不是建造最炫酷的系统,而是打造最有效的系统。
而有时候,“有效”恰恰意味着选择那个平淡、简单、聚合的方案。
那不是 vision 的失败,而是智慧的体现。
- 点赞
- 收藏
- 关注作者
评论(0)