找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3234

积分

0

好友

417

主题
发表于 3 天前 | 查看: 17| 回复: 0

描述架构演进的卡通插画

单体和微服务,究竟谁是那个“毒瘤”?它们与分布式、SOA之间到底是什么关系?面对具体的项目,我们又该如何抉择?最近我花了不少心思研究这些问题,总算理出了一些头绪,想和大家一起聊聊。

01 架构的发展历程

我始终觉得,要深刻理解一项技术,光看网上那些对比表格是远远不够的。关键得了解它诞生的背景:它究竟解决了什么问题,又带来了哪些新麻烦,最后又为何被替代。下面的内容,我参考了《凤凰架构》以及 Martin Fowler 等人的一些文章,我们一起来看看,历史的浪潮是如何一步步推动架构演进的。

1.1 原始分布式时代

第一个登场的竟然是“分布式”而不是“单体”,这有点反常识吧?但事实就是如此,分布式出现得比单体要早。实际上,“单体”这个概念,是在微服务流行起来之后才被“事后追认”的。在那之前,分布式早已登上舞台,并且成果不少。

1971年,Intel公司设计了世界上第一台微型计算机MCS-4,开启了微型计算机的时代。计算机逐渐从专业的科研设备,转变为企业生产工具。但问题也随之而来:计算机硬件有限的运算能力,直接制约了在单台机器上所能构建的软件系统的最大规模。 如果你生活在那个时代,相信也能想到最朴素的解决思路——“人多力量大”。于是,高校、研究机构和企业都不约而同地开始探索 “使用多台计算机共同协作来支撑同一套软件系统” 的方案。

说到分布式,就绕不开远程调用。当时,为了避免重演“UNIX版本战争”的悲剧,开放软件基金会与主流计算机厂商共同制定了DCE/RPC规范,它也被公认为现代RPC的鼻祖之一。因为基金会本身就在制定UNIX标准,所以DCE/RPC的设计哲学也带着浓厚的UNIX “简单优先原则”,其目标是:让开发人员不必过于关心他们调用的方法或访问的资源是在本地还是远程,尽可能透明。

然而,这个理想在当时面临太多技术难题。远程调用比本地调用复杂太多了:服务发现、负载均衡、熔断、隔离、降级、认证、授权……一系列问题亟待解决。令人敬佩的是,DCE从无到有构建了大量协议,确实在一定程度上做到了“透明”。但分布式还有一个致命伤——网络带来的性能损耗。

我们来推演一下:硬件性能不足 -> 采用分布式 -> 远程调用导致性能下降 (这反而与解决硬件性能不足的初衷相悖) -> 于是开发者不得不通过合并请求等方式,刻意降低网络损耗 (这时开发者必须意识到自己在写分布式程序,又与DCE追求的“透明”相悖) -> 最终,人的能力成为了软件规模的瓶颈。从这个角度看,早期的分布式探索并不能算成功,设计最终向性能妥协,使得“简单透明”成了一句空话。

打游戏时,叫人打不过怎么办?还有一个办法——氪金。20世纪80年代,摩尔定律稳定发挥,微型计算机性能以每两年翻一番的速度飙升。既然分布式充满了矛盾与妥协,那就加钱换更好的机器吧。于是,单体时代到来了。

描绘通过提升单机性能来解决问题的卡通形象

1.2 单体架构时代

在微服务出现之前,“单体”甚至不被认为是一种“架构”,因为它太“简单”、太“自然”了。以至于我想找一本专门讲单体最佳实践的书都很难找到。现在很多书把单体当“毒瘤”,甚至有人认为微服务比单体更先进,但事实果真如此吗?

首先,我们要明确单体是什么:“单体”仅仅表明系统中主要的调用都是进程内通信,不发生进程间通信,仅此而已。 那么,进程内调用是罪恶吗?显然不是。现实中不会有人对你说:“你这个Hello, World!程序不能用单体,因为单体是毒瘤。”

有个流行观点是“单体不便于扩展”。我们重点讨论一下这个说法。扩展无非从两方面看:性能扩展和功能扩展。

  • 性能扩展:这很简单,把一个服务部署多个副本,前面加个负载均衡器分摊流量,这不就扩展了吗?
  • 功能扩展:纵向上,我没见过哪个大型系统代码是不分层的。用过Spring的都知道,默认就按MVC分层以便扩展。横向上,我们也可以按功能拆分为不同模块(模块间可以不通信或少通信。注意:拆分成模块不等于不是单体,单体系统也可以有很多模块),各模块完全可以独立迭代。

从这个角度看,单体在“可扩展性”上并不输给微服务,甚至在开发、调试等方面还更有优势。当然,单体也有其局限。比如,一个C++系统要加入AI功能,显然Python更合适。虽然在C++里调用Python能实现,但远不如独立部署一个Python微服务来得优雅。所以,“单体服务不便于扩展”这个观点并不完全正确。

展示单体架构性能与功能扩展方式的示意图

真正的“罪恶”在于“大型的单体系统”。大单体的主要问题在于隔离性差。举个例子:不小心写了个导致崩溃的BUG。在微服务中,可能只影响一个模块及其依赖的功能。但在单体中,所有代码共享同一个进程空间,一处崩溃可能导致整个进程挂掉,影响范围就大多了。

简单来说:单体服务在同一进程内更简单、高效,其代价是牺牲了隔离能力和技术栈选择的灵活性。 这两点孰轻孰重,得看你的系统到底是“小卖部”还是“大超市”。

既然各有优劣,为何微服务后来成了潮流?因为互联网在发展。软件功能越来越多,开发团队规模越来越大,我们进入了“大兵团作战”时代。“墨菲定律”加上“黑天鹅事件”,对系统可用性的威胁越来越大。

构建一个大规模且可靠的系统非常难。根据墨菲定律,可能出错的事(BUG)就一定会出错。再叠加不可预测的黑天鹅事件,随着团队规模扩大,系统不可用的概率会无限放大。举个极端例子:假如一个公司有10万人,每人写出的模块可用性高达99.999%,但当他们协作写一个单体服务时,整体可用性就是99.999%的10万次方,约等于36.8%——这系统几乎不可用。更何况人本身是不可靠的。这时,单体隔离性差的缺点就暴露无遗。

微服务则将构建可靠系统的思路,从 “追求尽量不出错”转变为“承认出错是必然” ,通过拆分服务来限制异常的影响范围。可以用人体来理解:人体由无数不可靠的细胞(微服务)组成,但个别或部分细胞的死亡,并不会影响整个生命系统的运转。这就是用不可靠部件构建可靠系统的典范。

随着系统规模扩大,拆分单体势在必行。前辈们探索了多种拆分方式,其中比较有代表性的有三种:

1. 烟囱式架构:没什么高深理论,就是把一个系统拆成多个,且拆分后的系统相互独立、没有业务交互,因此也叫“孤岛式信息系统”。但问题在于,这种场景真的多吗?如果两个系统真没交互,那大概率一开始就会设计成两个独立系统。既然放在了一起,往往是因为它们共享了某些资源,比如存储、权限、账号等。

展示两个完全独立、无交互的“买菜系统”和“买书系统”

2. 微内核架构:既然烟囱式很少见,那就大方地把共享的数据、资源、公共服务集中起来,成为一个被所有业务系统依赖的“核心”。具体业务则以插件模块形式存在。关键在于“微”,核心只负责通用业务逻辑,不包含针对特殊情况的、复杂的自定义代码。 不是所有插件化都是微内核。例如MySQL的存储引擎是插件,但其Server层是核心功能,所以MySQL不算典型的微内核架构。

这种架构至今仍在使用,Eclipse IDE就是典型:下载的基础版只是个简单编辑器,安装插件后就能变成强大的开发工具。当然它也有局限:插件之间无法直接交互,只能通过内核通信。

保险行业也常用微内核架构。不同地区、时间、事件的理赔规则极其复杂,容易形成“大泥球”,牵一发而动全身。使用微内核模式可以有效解决这个问题。

展示微内核架构,核心系统被多种理赔业务插件环绕

3. 事件驱动架构:它解决了微内核架构中插件不能直接交互的问题。子系统之间通过一套事件队列管道通信,可以发布或订阅消息,实现完全解耦。这种架构历久弥新,今天的电商系统依然有其影子:下单、支付、发货、结算,就是由一系列事件驱动的。

展示事件驱动架构的流程图,生产者、路由、消费者与服务解耦

1.3 SOA架构时代

前面提到,单体和分布式是并行发展的。在单体探索如何拆分的时期,分布式系统也在曲折前进,SOA即将登上舞台。

刚接触“SOA治理”时,我也不太清楚到底“治理”什么。SOA看起来和微服务很像,区别不明显。SOA(面向服务的架构)的定义实在太多了,没有绝对权威的解释。但总结起来,至少两点是共识的:

  1. SOA中的“服务”,是包含执行完整、独立业务功能所需的代码和数据的单元,粒度比我们今天说的微服务要粗。
  2. SOA的目的是复用,并为此制定了一系列标准。服务间使用公共接口和架构模式,便于快速整合到新应用中,开发者无需了解底层连接细节。

这么看,SOA并没有解决新问题,它依然在应对分布式初期就预见到的困难。但SOA的解决方案更具体、操作性更强,并形成了一整套规范标准。相比之前的烟囱、微内核、事件驱动架构,SOA显得更“实在”。例如,它明确采用SOAP作为远程调用协议,明确使用企业服务总线(ESB)来实现子系统间通信等。从技术上讲,SOA成功解决了分布式环境下的主要技术问题。那它为何又被微服务替代了呢?

展示企业服务总线(ESB)作为中心枢纽连接各服务

因为SOA太重了。过于严格的规范定义带来了过度的复杂性,落地成本极高。以远程调用(RPC)为例:RPC最初的目标是让调用远程方法像调用本地方法一样简单,但今天大多数RPC技术已不再追求这个目标,因为通信是有成本的。Andrew Tanenbaum教授在1987年就指出,把本地和远程调用透明化处理,是方向性错误,反而会增加程序员负担。直到Sun公司总结出“通过网络进行分布式运算的八宗罪”,这个观点才被广泛认可,明确了 “RPC是语言层次的特征,而非系统层次的特征”

既然是语言层次的特征,编程语言又那么多,最理想的自然是有一套统一规范。这个规范的发展很曲折,我们关注一个历史性时刻:1998年,W3C发布SOAP 1.0来实现Web Service。SOAP曾风光无限,但其没落并非因为性能和易用性(虽然不完美),而是因为它过于“理想主义”,期望解决分布式计算中所有问题,为此定义了庞大的Web Service协议家族,学习使用成本极高。这种脱离实践的做法,注定了SOAP的衰落。而作为SOA的重要基石,SOAP的没落也意味着SOA架构的没落。

回顾“原始分布式时代”,Unix DCE的宗旨是“让远程调用像本地一样透明”。但到了SOA,已经背离了这一初心。于是,微服务时代到来了。

1.4 微服务架构时代

微服务真正崛起是在2014年,此时的它和9年前刚被提出时已有明显不同。最初,微服务是SOA的轻量版:既然SOA太繁重,那么微服务就尽可能抛弃SOA中所有可抛弃的约束和规范,回归简单透明的初心。当然,现在微服务已脱离SOA,成为一种独立的架构风格。Martin Fowler在《Microservices: A Definition of This New Architectural Term》中已有详细解释,这里不再赘述。

微服务和SOA到底有何不同?纠结这个意义不大,因为它们的关系很微妙。有人说ESB是SOA的特色,这过于武断了。微服务抛弃的是SOA中“可以抛弃的标准”,不代表它一定不用ESB(或其他组件)。微服务是自由的。我们只需知道:微服务提倡以“实践标准”代替“规范标准”,针对分布式服务的那些老问题,微服务不再提供统一解决方案,开发者可以根据实际情况自由选择。

微服务是普通开发者的狂欢(可以更自由),也是架构师的噩梦(要求能力更高)。微服务并非银弹,没必要神话它。分布式中的老问题——服务发现、跟踪治理、负载均衡、传输通信等,微服务一个都没避开,该解决还得解决。但在云时代,微服务迎来了新契机:云技术让微服务从单纯依靠软件解决分布式问题,发展到软硬协同应对,也称为“后微服务时代”。

当解决问题的思路不局限于软件时,事情就简单多了。软件可以命令伸缩,硬件难道就不能通过敲键盘就“变出”服务器、负载均衡器吗?氪金购买云服务确实能解决问题。虚拟化技术模糊了软件与硬件的边界,一旦硬件虚拟化足够灵活,许多与业务无关的技术性问题(如弹性伸缩)就可以下沉到基础设施层解决,让研发更专注于业务。这后来也催生了云原生等新概念,但本质上,它们追求的目标和微服务是一致的。微服务时代的成就,离不开虚拟化技术的巨大贡献。

微服务+云的模式,让普通程序员也能写出可用于生产的软件。但这同时也导致开发者出现分层:大部分普通程序员加上一小部分软件设计专家,形成金字塔结构。

1.5 无服务时代

回想一下,分布式架构之所以存在,最初就是因为单台机器的性能无法满足复杂系统的需要。那么,如果单台机器性能“无限”,所有问题不就迎刃而解了?以前这不敢想,但现在云计算的发展让这个设想接近现实。

所谓“无服务”(Serverless),主要涉及两点:后端基础设施和函数。后端基础设施(BaaS)提供日志、存储等支撑服务;函数(FaaS)则负责实现业务逻辑。我们无需考虑容量和算力,函数即服务。云计算目前提供的能力,已经让无服务架构初现端倪。

至此,架构演变的脉络已经理清。相信你也理解了每种架构出现的意义与淘汰的原因。接下来,我们聊聊更实际的问题:今天,我们该如何选择?

02 单体还是微服务

我们花了大量篇幅梳理架构演进,就是为了在历史背景中理解每种架构,从而解决今天的现实问题。直到现在,关于架构的争论依然存在,最激烈的莫过于单体与微服务之争。

先说结论:假如一个业务发展良好,当系统复杂度增加到一定程度,从单体迁移到微服务是必然的。但在此之前,我推荐从单体开始。 下面我们看看,是什么驱动着复杂系统向微服务迁移。

2.1 为什么要使用微服务

天下熙熙,皆为利来。单体如此简单自然,为何还要迁移到微服务?为了更好的性能吗?微服务可以灵活扩缩容,应对性能压力。但随着云计算发展,单体服务同样可以集群化部署、方便扩缩容,而且硬件越来越便宜,单体还减少了RPC带来的性能损耗。所以,仅为了性能而选择分布式,那是40年前“原始分布式时代”的目标。需求应该是“不这样不行”,而不是“这样也行”。 复杂系统迁移微服务,必然有非迁不可的理由。

2.1.1 当个人能力成为系统发展的明显制约

这个问题很好解释。前面举过极端例子:10万人写一个单体,假设每人模块可用性99.999%,整体可用性却只有36.8%。更何况,我们无法保证团队里每个人都专业、可靠。人是不稳定的,受情绪、健康等影响,难免出错。在单体架构下,系统“整体”与“部分”没有物理隔离,一旦出错缺乏有效阻断手段,少数专家也无法控制某个不起眼的BUG造成全局影响。

当团队人员水平参差不齐,或者团队规模已经非常大时,微服务是更好的选择。单体架构下,系统质量只能靠研发管理和流程来保障;而微服务则假定“系统一定会出错”,其架构本身追求独立与自治。可以让高水平专家维护关键基础设施和核心服务,其他非核心功能即使出错,也能依靠基础设施的自愈能力恢复,或者将影响控制在局部,保障系统整体高可用。

2.1.2 当技术栈成为系统发展的明显制约

语言没有绝对的好坏,但有是否合适。举个简单例子:近几年AI如火如荼,大家都想结合AI提升业务。但一调研就会发现,对于AI训练,Python的支持是其他语言难以比拟的。如果原有系统是C++写的,那么对不同技术栈、框架实现的功能进行分布式部署,就不是想不想的问题,而是无可避免的选择。当然,在C++里调用Python也能实现,但使用这些“奇技淫巧”带来的维护成本是难以估量的。

2.1.3 当组织架构成为系统发展的明显制约

康威定律大家应该都了解:设计系统的架构,受制于产生这些设计的组织的沟通结构。变化是永恒的,随着业务规模扩大,开发团队必然扩大,组织架构随之调整是自然的事。我们无法对抗康威定律,系统架构必须随之调整。举个例子:我要发布某个模块,找谁审批?通常是自己的领导。但在单体服务中,一个模块可能涉及多个团队,让所有相关领导审批,会导致发布效率极其低下,这是难以容忍的。

如何识别组织架构已成为制约呢?当所有决策都依赖某个人或某个会议,对某项功能无人愿意(或能够)负责,或者愿意负责的人成了决策的单点瓶颈时,就该考虑拆分服务了。

2.1.4 小结

我们必须认识到,微服务是有成本的。仅用表格对比微服务要解决的各种问题,可能感受不深。下面这张著名的图——Netflix在2014年公开的微服务调用关系图——或许更直观。

展示Netflix公司2014年复杂的微服务调用关系网

我相信,Netflix内部没人能完全理清这张网。这是一个“黑洞”。如果某个微服务要修改协议,很难弄清楚需要通知哪些依赖方进行兼容修改。

微服务的核心价值在于,拆分后的系统能让单个服务实现敏捷开发和独立部署,最主要的目的是对系统进行有效的物理隔离,通过一系列自治手段,用不可靠的微服务构造出可靠的系统。然而,微服务在解决单体问题的同时,又带来了服务治理、链路追踪、数据一致性等新问题。如果权衡不好,反而会导致生产力下降。任何系统改造的根本动力都是“收益大于成本”。因此,“对于中小型系统,单体架构就是最好的架构”。只有当业务发展到一定阶段,单体与微服务的生产力曲线出现交叉点时,微服务化才是有收益的。至于如何判断这个交叉点,可以审视是否出现了以上三种情况。

展示系统复杂度与效率关系的曲线图,标出单体与微服务的平衡点
(图源《凤凰架构》)

2.2 使用微服务的必备条件

即使有充足理由迁移到微服务,也不能立刻行动,还要看团队是否具备相应条件,贸然迁移可能适得其反。

2.2.1 组织中具备对微服务有充分理解的专家

在讲微服务时代时提到,微服务给予编码者自由,但对架构师提出了史无前例的高要求。在实际团队中,并非人人都是架构师,大多数人更关注业务代码,而非扩展、认证、隔离等非功能性需求。但如果使用微服务却不关注这些问题,将是灾难性的。

举个例子:服务间如何路由?如何进行权限认证?超时和重试策略如何配置才能避免雪崩?流量变化时以何种策略扩缩容?这些问题不解决,会直接影响系统可用性。微服务架构团队常遇到这种情况:服务划分不合理导致高扇入扇出,加个字段要改N个模块、测N个接口,效率极低。

因此,要享受微服务的好处,首先需要有专家能识别其中风险,设计出可靠的架构。如果整个团队缺乏能在微服务架构中撑起系统主干的专家,那么强行迁移带来的收益,很可能无法抵消复杂度增加所导致的成本。

2.2.2 系统应具有以自治为目标的自动化与监控度量能力

我们期望团队拥有这样一个资产管理系统:能清晰看到所有资产、服务运行状况以及潜在风险。切换到微服务意味着资产成倍增长。回顾一下:微服务承认“错误必然发生”,因此需要一系列手段来用不可靠部件构建可靠系统。其前提是,系统能够监控自身健康状态,并实现自动恢复。也就是说,我们需要朝着自动化运维的目标迈进。

在缺乏基础设施的情况下使用微服务风险极大。想象一下系统故障:单体时可能只需查看少数几个模块;拆分成几百个微服务后,如果没有有效的监控和链路追踪,定位问题根源将如大海捞针。

2.3 如何实践微服务

如何实践微服务是个宏大话题,远非一篇文章能承载。相关的书籍就有《微服务设计》、《微服务架构设计模式》等很多。这里我只简单提几个要点供讨论:

  • 如果团队规模较小,在开始一个新系统时,我建议坚定不移地选择单体服务。这时,团队应对做出此决策的架构师给予充分信任。现实中,立项时常为业务定下宏伟目标,但从技术角度,仍应立足当下,随业务发展及时调整架构。切不可在业务规模扩大需要拆分时,反过来指责架构师:“当初就该用微服务,单体太落后。”客观想想,与一开始就用微服务相比,前期使用单体所节约的工作量,比起未来迁移的成本,往往是划算的。我们应该有这样的意识:我们无法设计出一个永远不变的完美架构,架构一定是随业务发展而不断演进的。

  • 如果团队规模较大,一开始就由多个小团队共同开发一个系统,那么我们不应对抗康威定律。应围绕业务能力将系统划分为相对独立的服务,由不同小团队负责,服务间交互采用统一规范。而且,推荐这些服务是去中心化的,即每个团队在技术栈选择上有一定自主权。推荐每个服务管理自己的数据库,即使不同服务可能涉及相同实体,但各自所需属性有差异,自己维护更利于独立迭代。实际上,每个小团队都在维护一个“子系统”。那么,参考上一点 “如果团队规模较小” 的结论,各个小团队在构建自己的服务时,也可以从单体架构开始,没必要一开始就进一步拆分成更小的微服务。

我所在的团队也一直在探索和实践微服务。我们某个业务的核心功能,到现在依然是单体服务。该业务已发展三年,至今尚未因为使用单体而在某一点上明显拖慢效率。

展示组织架构与服务划分关系的示意图,阐述康威定律

03 尾巴

回到最初的问题:单体和微服务,谁是“毒瘤”?都不是。它们只是在特定历史阶段下,适应潮流而生的普通架构,没有高低贵贱或先进落后之分。在今天,它们也只是我们结合业务与团队实际情况,进行利弊权衡后可供选择的方案之一。

关于架构选择、团队协作的更多实践和讨论,欢迎来云栈社区与更多开发者交流。

参考资料

  1. 《凤凰架构:构建可靠的大型分布式系统》周志明
  2. 《Microservices: A Definition of This New Architectural Term》 Martin Fowler、James Lewis



上一篇:企业级统一消息推送系统架构设计:从混乱到统一的实战指南
下一篇:流计算Oceanus+Elastic Stack:百亿级大数据实时监控系统搭建指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-3-10 08:03 , Processed in 0.422132 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表