2008 年 8 月,Netflix 的单体 Oracle 数据库损坏,导致连续三天无法发货 DVD。这场事故让他们看清了单体 Java 应用(所有功能打包在一个巨大的程序里)的脆弱:一个模块的 bug 可以拖垮整个网站,一次小功能上线需要协调数百名工程师同时停工。2009 年,他们做了一个痛苦的决定——迁上云、把整个系统拆成几百个独立的小服务。这场迁移花了约七年,直到 2016 年 1 月关闭最后一个自有数据中心才彻底完成,最终演化成上千个松耦合的微服务。
这场迁移让 Netflix 成为了微服务架构最有影响力的布道者,也确立了这种架构风格作为"互联网规模软件"的工业标准。
一个名字的由来
"microservices"这个词比它指代的实践年轻得多。2011 年 5 月,一群软件架构师在威尼斯附近的研讨会上,用 microservice 给他们各自都在探索的一种共同风格命名;这个词在 2012 年 5 月被正式采纳。同年,James Lewis 在克拉科夫的 33rd Degree 大会做了题为《Microservices — Java, the Unix Way》的案例分享。2014 年 3 月,James Lewis 与 Martin Fowler 合写了《Microservices: a definition of this new architectural term》,给出此后被反复引用的定义:把单个应用做成一套小服务,每个服务跑在自己的进程里、用轻量机制(通常是 HTTP API)通信、围绕业务能力构建、可独立部署、可各用不同语言和数据存储、几乎不需要中心化管理。
但实践远早于命名。最常被引用的源头是亚马逊:2002 年前后,贝索斯发出著名的"API 强制令"——所有团队只能通过服务接口暴露数据与功能,团队之间只能通过这些接口通信,禁止直接读对方数据库、禁止共享内存、禁止任何后门,而且每个接口从设计起就要做到"可对外开放"。这条铁律逼出了面向服务的内部架构,也直接孕育了后来的 AWS。
拆分的方法论同样早有准备。Eric Evans 在 2003 年的《领域驱动设计》提出限界上下文(bounded context):每个上下文有清晰的边界和明确的语言,边界之外的概念与它无关。微服务边界常常沿着限界上下文来划——但 Evans 本人提醒,把"一个 bounded context = 一个微服务"当成铁律是一种过度简化:限界上下文界定的是"最大可能的服务",一个上下文内部还可以再细分出更小的服务。
破除误解:微服务不是"把单体切开"
最常见的误解是:把一个单体应用按功能模块切分,每个模块独立部署,就得到了微服务。
这个理解在技术上不错,但忽略了微服务架构的组织论前提。Conway 定律指出:系统的架构往往镜像设计它的组织的沟通结构。微服务架构要求每个服务由一个自治的小团队完全拥有(从开发到部署到运维),技术拆分必须伴随组织拆分,否则只是增加了分布式系统的复杂性而得不到独立部署的好处。
单体 vs 微服务
单体架构(Monolith):所有功能在同一进程内运行,共享内存和数据库。
优点: - 开发简单:一个代码库,一次部署,函数调用跨模块是纳秒级 - 调试直接:完整的调用栈,事务边界清晰 - 适合小团队、早期阶段
缺点: - 扩展全或全不:某个模块需要更多资源,必须整体扩容 - 技术锁定:所有模块必须用同一语言、同一框架 - 部署风险集中:任何变更都影响整个系统
微服务架构(Microservices):每个业务能力是独立进程,通过网络(HTTP/REST 或消息队列)通信,各有自己的数据存储。
优点: - 独立部署:用户服务改动不影响支付服务 - 独立扩容:把有状态的服务单独水平扩展 - 技术异构:推荐系统用 Python,支付用 Java,各用各的最佳工具
缺点: - 分布式系统的全套复杂性(网络延迟、服务发现、部分失败) - 调试困难:跨服务追踪一个请求需要分布式追踪工具(Jaeger、Zipkin) - 运维成本急剧上升
核心模式
API 网关:对外暴露统一的入口,负责认证、限流、路由,把外部请求分发给内部服务。内部服务的接口变化不对外暴露。Netflix Zuul、Kong、Nginx 是典型实现。
服务发现:服务实例动态增减,不能用固定 IP 配置。服务注册中心(Consul、Eureka、Kubernetes 的 DNS)让服务在启动时注册自己,调用方通过名字而非 IP 找到服务。
熔断器模式(Circuit Breaker):当下游服务连续失败超过阈值,熔断器"断开",快速返回错误而不再等待超时,防止故障级联(cascading failures)。Netflix 的 Hystrix 库把这一模式普及;Martin Fowler 2014 年的文章将其系统化。
边车模式(Sidecar):在每个服务容器旁注入一个代理容器,承担服务发现、负载均衡、TLS、重试、遥测等横切关注点——服务本身不需要实现这些,只关注业务逻辑。Istio、Linkerd 基于此模式构建服务网格(Service Mesh)。
数据管理困境
微服务架构要求每个服务拥有自己独立的数据存储(数据库分离原则)——不同服务共享同一个数据库会导致隐式耦合,违背独立部署的初衷。但这带来了棘手问题:
跨服务事务:单体内的事务是数据库保证的 ACID 操作;微服务中跨两个服务的操作没有天然的分布式事务边界。Saga 模式(把长事务拆成一系列补偿操作)是常见解法,但把部分失败处理的复杂性转移给了应用开发者。
数据一致性:每个服务有自己的数据库,全局数据视图是最终一致的。这对很多业务场景(如"用户账户余额")是可接受的,但对某些场景(如"银行转账")是根本障碍。
代价与争议
微服务把进程内的函数调用换成了跨网络的远程调用,于是一头撞进"分布式计算的谬误"(Fallacies of Distributed Computing)。这份清单源自 Sun Microsystems:L. Peter Deutsch 在 1994 年前后列出七条,James Gosling(Java 之父)约 1997 年补上第八条。八条假设——网络可靠、延迟为零、带宽无限、网络安全、拓扑不变、只有一个管理员、传输成本为零、网络同质——逐条都是错的。单体里的函数调用恰好可以装作它们成立;把单体拆成微服务,本质上是把这八个谎言从"可以忽略"变成"每一条都得正面处理"。
复杂性爆炸:Sam Newman(《构建微服务》作者)在 2022 年明确警告:大多数组织过早采用微服务,在系统规模和团队规模还不足以支撑的时候引入了所有分布式系统的复杂性,却没得到多少好处。他建议以单体开始,在遇到真实瓶颈时再逐步拆分。Martin Fowler 在 2015 年的《MonolithFirst》里给出同样的经验法则:几乎所有成功的微服务案例,都是从一个长大到难以维护的单体演化拆分而来;而几乎所有"一开始就按微服务从零搭建"的系统都陷入了严重麻烦。他的建议是先把单体的模块边界和数据存储设计干净,让日后切分变得简单。
"纳米服务"反模式:服务拆得过细——每个服务只有几百行代码——网络通信开销超过了业务逻辑本身,形成"分布式单体"(Distributed Monolith):拆开了但没法独立部署。
调试地狱:一个请求可能经过 10 个服务,每个服务有自己的日志,追踪一个线上 bug 需要完善的分布式追踪基础设施,否则比单体难得多。
跨域连接
- 科斯:企业为什么存在,与某个功能为什么该留在同一进程里,是同一个问题。留在内部用命令协调,拆出去用契约协调,边界应划在两种协调成本相等之处。推论解释了正文的分布式单体:当接口不稳定、每次变更都要多方同步发布,契约成本超过了自治收益,拆分只买到了网络延迟。
- API 设计:独立部署的充要条件是任意两个版本能共存。因此接口演进必须只增不删、旧字段保持可选,否则部署顺序被锁死,拆分带来的自治立刻蒸发。契约测试的作用正是把这条约束变成可执行的检查——它验证的不是「接口能用」,而是「消费者依赖的那部分没有被生产者悄悄改掉」。
- 联邦制:微服务与单体的取舍,与联邦制和单一制属同一类问题:把决策权下放给自治单元能提高局部适应速度,代价是需要一套接口与仲裁机制,并要承受协调失灵。推论很硬——若没有配套的自治权,也就是团队真正拥有从开发到运维的全流程,拆分只增加协调成本而不产生自治收益。
- 社会网络分析:服务调用图是一张有向网络,故障沿边传播,超时与重试还会放大流量。度分布通常极不均匀,少数高入度服务承载了绝大部分路径。可检验的推论是:熔断、限流与舱壁的部署优先级应按调用图上的中心性排序,而不是按业务重要性排序,这两个次序经常并不重合。
- 数据库事务:单体里的原子性由一个存储引擎兜底,跨服务后没有共同的提交协议,补偿操作只能在失败之后反向执行,无法隐藏中间状态。推论是业务必须显式承认部分完成:订单要有待支付态,库存要有预占态。凡是假装最终一致不存在的设计,都会把不一致以客服工单的形式暴露出来。
参考文献
- Fowler, M. & Lewis, J. Microservices: a definition of this new architectural term. martinfowler.com, 2014.(定义微服务架构的奠基文章,并记述了 2011 年威尼斯研讨会的命名经过)
- Evans, E. Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley, 2003.(限界上下文的原始出处)
- Fowler, M. MonolithFirst. martinfowler.com, 2015.("先单体后微服务"经验法则)
- Deutsch, L. P. & Gosling, J. The Fallacies of Distributed Computing. Sun Microsystems, 1994–1997.(分布式计算的八条谬误)
- Izrailevsky, Y. Completing the Netflix Cloud Migration. Netflix Technology Blog, 2016.(Netflix 七年迁移完成的官方记述)
- Burns, B. et al. Design Patterns for Container-Based Distributed Systems. USENIX HotCloud, 2016.(边车等容器模式的学术来源)
延伸阅读
- Newman, S. Building Microservices. 2nd ed., O'Reilly, 2021.(微服务实践最权威的参考书)
- Richardson, C. Microservices Patterns. Manning, 2018.(Saga、CQRS 等模式详解)
- Nygard, M. Release It! 2nd ed., Pragmatic Programmers, 2018.(熔断器、隔板等弹性模式的工程实践)