微服务架构(MSA)是如何诞生的?

发布时间:2026/6/1 9:02:55

微服务架构(MSA)是如何诞生的? (《构建微服务》是广为人知的MSA入门书籍)在开始之前...我的主页 MAKONEA 也是基于 MSA 构建的在外包项目中请用微服务架构来开发的需求也越来越多。现在这个概念似乎已经形成了理所当然就该这样做的氛围但一开始并非如此。本文将探讨MSA为何出现是为了解决什么问题而被选择的让我们在历史背景中追溯这一演变过程。技术选择始终诞生于前一种技术的局限与失败之上。要真正理解MSA在此之前我们需要先了解当时存在哪些架构方式以及这些方式为何存在不足。(传统3-tier架构单体应用架构示意图)1. Monolith单体架构许多 Web 应用传统上采用 3-tier architecture 进行设计。表示层负责界面展示业务逻辑层负责规则与处理数据层负责存储与查询。어떤 조직이 시스템을 설계하면,그 시스템의 구조는 결국 그 조직의 커뮤니케이션 구조를 반영하게 된다.— Melvin Conway, “How Do Committees Invent?”这里还有一点值得关注。为什么偏偏是3-tier architecture如此广泛流行Sam Newman在《Building Microservices》中引用Conways Law对这一问题作出了解释。系统的结构反映了构建它的组织的沟通结构。过去的IT组织往往按技术能力划分分为数据库团队、后端开发团队和前端开发团队。3-tier architecture正是这一组织结构的直接映射。也就是说3-tier的流行并非因为它在技术上是正确答案而是因为它是针对当时组织结构所做的最优设计。这里有一点很重要。这种层次划分是逻辑上的分离而非部署单元的分离。无论代码被多优雅地拆分成3-tier architecture或是涂抹了多少设计模式一旦按下构建按钮所有内容就会被打包成一个二进制文件运行在一个进程之上。这就是Monolith。它源自希腊语单一的mono加上石头lith意指由一整块石材构成的庞大结构。Monolith的问题并非没有结构。其内部结构可以整理得井井有条。问题在于变更、部署、扩展和故障的边界全都被绑定在同一个应用单元之中。而这种捆绑在规模较小时反而是一种优势。网络延迟趋近于零因为模块间的连接是内存函数调用而非网络调用。无需考虑分布式事务单一数据库即可保证完整的数据一致性。代码流程可以在一处追踪。在初期产品、小型团队和快速验证阶段这种简洁性是最大的优势。MSA的布道者们编程训练营和YouTuber们总在诱导你从一开始就把一切拆分得细碎但在业务生存能力尚未得到验证的情况下就去承担网络分区和基础设施的复杂度无异于自寻死路。从常识来看在没有用户的情况下有必要刻意将系统设计得更复杂吗完全没有。牛棚里一头牛都没有没有任何理由把牛棚建得复杂。理解MSA时首先需要摒弃的误解是Monolith是陈旧且糟糕的架构这一观念。Monolith并非失败的架构而是一种在系统规模超过某个临界点后成本结构随之发生变化的架构。成本上升的节点有三个。Deployment coupling部署耦合。即使只修复了一个登录模块的bug也必须重新部署整个订单模块。每次部署的风险范围覆盖整个系统。部署周期随之变长而更长的周期又迫使更多变更被打包在一次部署中风险因此进一步放大。这是一个恶性循环。Scaling coupling。即使流量集中在商品查询上也必须连同支付服务器一起进行扩容。因为所有功能都在同一个进程中没有办法只针对实际承受负载的组件进行选择性扩展。团队耦合。团队规模扩大后所有人都在同一个代码库上操作。合并冲突、构建等待、谁动了这段代码这类问题变成家常便饭。团队越大在同一个代码库上协作的成本就会呈指数级增长。在业务生存能力尚未得到验证的阶段如果这三个问题还不存在那么Monolith就是正确的选择。MSA是在这些成本真正难以承受时才打出的那张牌。2. SOA——第一次尝试与失败Monolith局限性问题的第一次正式尝试是SOA面向服务架构。Mark Richards 同样评价SOA是庞大、昂贵、复杂且实施周期过长的架构。2000年代初企业级领域主导了这一运动其核心理念很简单。将系统拆分为服务让这些服务通过标准化接口进行通信。方向是对的问题出在实现方式上。当时SOA确实席卷了整个业界。许多企业为了提高可复用性和跨组织协作纷纷引入了这一架构。然而结果却与预期大相径庭。设计与实现耗费了过多时间结构日趋复杂成本也大幅攀升。最终大量项目以失败告终SOA逐渐被业界所抛弃。借用Mark Richards的说法SOA是一种庞大、昂贵、复杂且实现周期过长的架构。这一点至关重要。问题并不在于理念本身而在于实现这一理念的方式。SOA的标准技术栈由SOAPSimple Object Access Protocol、WSDLWeb Services Description Language和ESBEnterprise Service Bus组成。理论上ESB是集中协调服务间通信的智能中间件其核心理念是在一处统一处理路由、转换和编排。但现实却大相径庭。ESB无节制地吸收业务逻辑和路由规则愈发臃肿各个服务也因此对ESB产生了强烈的依赖。这便是所谓智能管道、哑端点Smart pipes, dumb endpoints的陷阱。服务本身的内聚性被破坏系统的控制权再度向中心集中。本想构建分布式系统而拆分了Monolith结果却在中央重新竖起了一个名为ESB的庞大单点故障SPOF, Single Point of Failure。SOAP与WSDL是一场XML Schema的噩梦。每新增一个服务就需要编写WSDL、生成存根stub再花费数天时间协调双方的契约。应能轻松添加服务这一SOA的承诺与现实之间存在着巨大的鸿沟。SOA的失败本质上是哲学层面的失败而非技术层面的失败。服务虽然被拆分但耦合度并未真正降低试图减少复杂性却又制造出ESB这一新的复杂性。微服务领域则选择了另一种不同的方式“智能终端哑管道”。应用程序被设计成尽可能松散耦合同时每个服务仍保持较高的内聚性。——马丁·福勒这次失败的教训成为此后MSA的前车之鉴。这也正是MSA将智能端点、哑管道Smart endpoints, dumb pipes奉为核心原则的根本原因。3. Amazon的转折点——2002年Bezos Mandate就在SOA在企业级市场走向失败的2002年Amazon内部流传着一封具有历史意义的邮件。Steve Yegge公开的亚马逊内部指引回顾这便是如今被称为Bezos Mandate或Bezos API Memo的内部指令其核心内容如下。所有团队必须通过服务接口对外暴露数据和功能。各团队只能通过该接口进行通信。不允许其他通信方式。禁止直接链接、直接读取数据库以及共享内存模型。使用何种技术均可HTTP、Corba、Pubsub随便什么都行。无一例外。不遵守此规定者一律开除。这条规则的重要性不在于技术栈本身而在于其强制执行力。彼时Amazon 正在一个庞大的单体系统之上持续扩张。各团队直接读取彼此的数据库以无内部 API 的方式直接链接库来堆砌功能。Bezos Mandate 切断了这种连接方式要求所有功能置于 API 之后并强制将团队间通信转变为基于网络的服务接口。起初这不过是一条旨在降低内部耦合度的规定。然而这一变革使 Amazon 开始将自身内部功能视为服务来思考。正如 Yegge 所言此后 Amazon 转型为一个以 services-first 方式审视所有设计的组织。沿着这条脉络基础设施功能也成为可通过 API 提供的服务并为 2006 年 S3 与 EC2 的发布奠定了 AWS 的根基。那条为打破单体架构而设立的内部规则最终成为云计算时代的重要基石之一。4. Netflix——用混沌铸就韧性2008年Netflix遭遇了一次严重的数据库损坏事故。(Netflix的回顾文章)Netflix在2016年的一篇回顾文章中提到2008年的数据库损坏事故导致DVD配送中断长达3天他们表示这一事件成为了历时7年的AWS迁移之旅的起点。整整3天DVD配送陷入停滞。这次事故给Netflix工程团队留下了两个深刻的教训。第一中心化的单一系统中一个故障就可能导致整个服务中断。第二故障是无法预防的必须构建能够在故障中依然存活的系统。2009年Netflix开始将数据中心迁移至AWS。这不是一次简单的基础设施迁移而是一段历时7年、将单体系统拆解为数百个微服务的漫长旅程。在这一过程中Netflix开发了几个核心工具。周一Netflix 开源了一款名为“混沌猴子”的软件。这个工具会随机终止构成其流媒体服务的虚拟机用于模拟服务在日常运行中会碰到的小规模故障。这意味着任何人都可以使用这个工具甚至修改它的源代码来满足自己的需求。——《连线》杂志《Netflix 用猴子“祸害”亚马逊现在你也可以》2012 年Chaos Monkey.一种在生产环境中随机终止实例的自动化工具。其理念源于验证系统能否承受故障的最好方法就是真正制造一次故障。Chaos Monkey的存在本身就促使工程师们编写以故障为前提的代码。Eureka.服务注册中心。解决了在数百个服务实例动态启动与下线的环境中各服务如何相互发现的问题。(Netflix博客中介绍Hystrix的文章)Hystrix.熔断器Circuit Breaker库。当依赖服务变慢或宕机时快速失败Fail Fast对该服务的请求防止故障扩散至整个系统。Netflix于2012年将这些工具以开源形式公开发布。这套被称为Netflix OSS的工具集成为当时如何在生产环境中运营微服务的参考实现。Netflix所创造的这些模式此后成为MSA的标准词汇。5. Microservices——正式命名之日Netflix、eBay、Amazon、LinkedIn、Google这些运营大规模系统的组织以不同的名称用相似的方式拆解着各自的系统。这种模式需要一个统一的名称。2011年5月聚集在意大利威尼斯附近的软件架构师们分享了各自发展出的新架构模式并开始将其称为microservice。 此后James Lewis于2012年在技术大会上正式发表了这一概念并于2014年与Martin Fowler共同撰写的文章中使这一概念最终确立为行业的标准术语。由各个具有单一职责的小型服务组成的集合通过进程隔离并以HTTP API等轻量级机制进行通信每个服务可独立部署可以与其他服务使用不同的语言和不同的数据存储 (Polyglot)相较于集中式编排优先保障各服务的自治性值得注意的是这篇文章并非发明了MSA。Amazon、Netflix、eBay已经运营这一模式多年。Fowler与Lewis的贡献在于为行业提供了共同的语言。有了名称之后社区随之形成模式也迅速扩散开来。同样是在2011年Heroku的Adam Wiggins发布了12-Factor App方法论提出了在云环境中构建SaaS时应遵循的12条原则。代码库、依赖项、配置、后端服务、无状态进程等这些原则明确规定了MSA实际运作所需的运营基础。6. 容器革命——基础设施跟上了步伐将MSA作为概念理解与实际运营之间存在巨大差距。部署、隔离和版本管理数百个服务对于传统基础设施来说过于复杂且成本高昂。2013年Docker解决了这个问题。Hey everyone... Im Solomon, I work at dotCloud[00:08]... weve been working on open sourcing that [Linux containers] and we havent shown it to anyone[02:06]... this is actually the first time we show anything outside of the dotCloud office so its probably going to blow up on me.[02:11]- Solomon Hykes,The future of Linux Containers (PyCon 2013 Lightning Talk)大家好……我是在 dotCloud 工作的 Solomon[00:08]……我们一直在做将 Linux 容器开源的工作……还从未向任何人展示过[02:06]……这是第一次在 dotCloud 办公室以外的地方进行演示所以可能会当场翻车。[02:11]Solomon HykesSolomon Hykes在PyCon 2013上用一个5分钟的演示介绍了Docker当时很多人的反应是不知道这是什么但感觉很重要。容器这个概念本身并不新鲜Linux的cgroups和namespaces早已存在。Docker的创新在于它将容器变成了开发者真正能用得上的工具。Docker出现之后将MSA的每个服务打包为独立容器成为可能。依赖项被隔离在镜像内部同一镜像可以在开发环境和生产环境中以完全一致的方式运行。随之而来的是下一个问题如何管理数以百计的容器2014年Google发布了Kubernetes。We present a summary of the Borg system architecture and features, important design decisions, a quantitative analysis of some of its policy decisions, and a qualitative examination of lessons learned from a decade of operational experience with it.我们对Borg系统架构与特性、重要设计决策、部分策略决策的定量分析以及十年运营经验所得教训的定性考察进行了总结。参考文献Large-scale cluster management at Google with Borg (2015)事实上Google早在十余年前就已在内部运行着一套名为Borg博格的系统每周负责调度和管理数十亿个容器。Kubernetes正是Borg的精神传承。Docker与Kubernetes的组合使MSA真正成为可落地的方案。那些此前只有拥有Netflix级别基础设施团队才能实现的事情如今小型团队也可以做到了。题外话名称中隐藏的隐喻与英语世界的文字游戏Kubernetes这个词本身在古希腊语中意为舵手Helmsman或船长。其中蕴含着Google宏大的世界观与隐喻。Docker的Logo让人联想到载着集装箱在海上游弋的鲸鱼船舶。而Kubernetes的寓意则是让漂浮在这片海洋上成百上千个集装箱Docker不相互缠绕、顺利抵达正确目的地扮演指挥整个船队的舵手角色。实际上其官方Logo也正是蓝色的船舵Steering Wheel造型。Kubernetes通常也被称为k8s这是因为K与s之间恰好有8个字母u-b-e-r-n-e-t-e所以将其缩写为k8s。在英语环境中通常发音为K-eights。事实上这种缩写方式是IT行业由来已久的惯例称为数字缩略词Numeronym。由于每次都要输入长单词太过麻烦便只保留首尾字母中间用数字代替。i18ninternationalization国际化→ i 18个字母 na11yaccessibility可访问性→ a 11个字母 yo11yobservability可观测性→ o 11个字母 y不过广泛用于轻量级本地/边缘场景的K3s并非缩写。最初创建 K3s 的 Rancher Labs现为 SUSE的开发者们设定了这样一个目标打造一个将 Kubernetes 的内存占用和二进制文件大小精确缩减为原来一半的版本Kubernetes 共10个字母其一半是5个字母按照前面提到的数字缩写规则压缩这5个字母就得到了K 3个字母 s k3s这一神奇的逻辑。这就是为什么非英语母语的开发者学习开发会如此艰难。7. 现状与下一个挑战随着MSA的普及新的问题逐渐浮出水面。可观测性Observability。当一个请求经过10个服务处理时如何判断延迟发生在哪个服务。在单一进程中一个栈追踪就能看清全貌而在MSA中痕迹会在每个服务边界处中断。分布式追踪Jaeger、Zipkin、OpenTelemetry正是为解决这一问题而生的工具。服务网格Service Mesh。当服务数量增多时在每个服务中分别实现服务间通信策略加密、重试、超时、熔断器会非常低效。Istio、Envoy、Linkerd 将这些逻辑抽离为 Sidecar 代理在服务代码之外统一处理。分布式单体Distributed Monolith。这是MSA最常见的陷阱。将服务拆分成多个之后如果这些服务以同步方式形成长链调用即A调用B、B调用C、C调用D实际上一个请求会串行地穿越所有服务。一旦某个服务变慢整条链路就会阻塞。这不过是把Monolith搬到了分布式环境中耦合度丝毫没有降低。这个陷阱之所以重要是因为MSA的核心目标「独立部署」与「独立扩缩容」都取决于服务边界是否划分正确。边界划错了只会得到分布式带来的复杂性却享受不到任何好处。这里有一点很重要不能把MSA简单地看作「更好的架构」。从Monolith到SOA再到MSA的演进并不是一个寻找更优解的过程而是在每个阶段选择不同类型复杂性的过程。编程的本质是缩小可选的状态空间架构亦然。每写下一行代码都是在限制自由度而编程正是不断施加这种限制、并衡量它与我们需求契合程度的过程。Monolith放弃了部署与扩缩容的自由度换来了简单性SOA尝试拆分服务却选择了集中化控制MSA放弃了集中化代价是承担运维复杂性。也就是说MSA并非「更好的结构」而是选择了另一种约束的结果。结语从历史中读出的规律如果用一句话概括MSA的历史那就是从Monolith的耦合问题出发规避了SOA过度集中化的弊端最终在云与容器基础设施之上以实用的形态落地的架构。从历史中可以读出一种规律。时代核心技术解决的问题引入的问题2000年代初Monolith简单性、快速开发Deployment coupling / Scaling coupling / Team coupling2002~2008SOA ESB服务拆分尝试ESB 中心化SOAP 复杂性20092013MSA 早期Netflix、Amazon实现真正的独立部署与扩缩容运维复杂性可观测性缺失20132015Docker Kubernetes基于容器的基础设施标准化编排学习成本2016~至今Service Mesh、Observability服务间通信策略 可见性复杂性增加每一层都在解决上一层问题的同时创造了新的问题。技术演进始终以这种方式推进。因此如果有人问我们也应该转向MSA吗这个问题本身就问错了。架构领域不存在无条件的正确答案也没有所谓的银弹Silver Bullet。正确的问题应该是这样的我们当前的业务规模和组织结构是否迫切需要在部署与扩展上获得更大的自由度以至于值得为MSA所要求的巨大运营复杂性买单如果马厩里一头牛都没有Monolith的简洁性就是正确答案。但如果流量与开发团队爆炸式增长已经到达无法承受原有耦合度的临界点那时就必须主动选择容器与分布式系统的复杂性。归根结底设计系统架构并不是寻找完美结构的旅程而只是明智地选择当前组织能够承受、或者必须承受的痛苦类型。但人类总是在不断重复错误的选择。在没有问题的情况下却因为看起来更先进而引入MSA在问题已经明确的情况下却因为习以为常而坚守Monolith。技术是一个选择的问题但大多数失败并非源于选择本身而是源于判断失误。

相关新闻