跳到主要内容

不要把你的分布式单体叫做微服务

内容修订

  • 2025/05/25:让“单体(Monolithic)和分布式”部份内容更易读一些
用语
解释
沟通结构为了达到某个目的,组织中的部门、团队和人员必须进行沟通。这些沟通所形成的依赖拓扑结构就称为沟通结构。

单体(Monolithic)和分布式

一个典型的应用软件系统主要包含 3 个组成部份:用户界面(如:GUI、Web)、服务API(请求处理)、数据层(状态持久化)

单体下,系统的各个组成部份会被捆绑在一起,并运行在同一条进程上。这种设计的显著优势是性能好、一致性强。因为无需进行网络通信。而且测试、部署、监控等方面都较为简单。但问题在于缺乏架构设计情况下,单体很容易随着业务迭代而变得复杂。因为产生耦合的成本很低,导致单体到最后经常会演变成大泥球1。另外,当系统需要满足服务水平协议(SLA)时,单体可能会难以胜任。因为即使只是修改一小处地方,它也需要重新构建和部署整个系统;这会显著降低服务质量。由于其技术栈的单一性,开发者很难可以为问题选择适合的解决方案;该问题导致容易滋生技术债务。在云计算环境中,其劣势会更加明显。因为完整系统的代码量一般较大,且通常单体都会存在状态设计;这导致冷启动和伸缩性方面表现不佳。基于上述原因,单体目前在业界貌似逐渐成为了一种“罪恶”(尽管个人并不完全认同)。因为经常可以看到,即便是小公司小团队,他们的项目一开始就不是基于单体来设计的。他们更愿意使用分布式来解决问题。所谓的分布式,就是将一个完整的系统拆分成多个服务组件,然后让其运行在各自的进程上。相较与单体,分布式拥有更好的伸缩性、可用性、扩展性和弹性(可靠、容错)。而且服务组件可以根据自己的问题域来选择合适的技术方案。但这些好处显然并非毫无代价,因为分布式网络通信比想象中要复杂得多;特别是当你需要满足性能或一致性需求时。但遗憾的是,这一点往往会被一些缺乏架构设计经验的人忽视,最终导致分布式单体的出现。所以如果你正在设计(或维护)一个分布式系统,那么个人建议是要时刻紧记分布式设计谬误

分布式单体是指仅在形态上呈现为分布式,而实际上服务组件之间存在各种耦合性。该问题实际上非常严重,因为它即不能解决单体存在的问题,还引入了分布式的缺点(如:通信性能损耗、一致性被削弱)。因为服务组件之间存在较高的耦合性,所以当其中一个被修改时,其余的(至少大部分)都需要进行修改;这使得系统维护成本大大增加。之所以会出现该问题,除了缺乏架构设计经验之外,还缺乏对分布式有效性前提的认识。前面已经提到,分布式实际上比单体复杂得多;它对团队的设计和开发能力都有较高的要求,所以团队应该有足够的能力和资源来应对相关的复杂性。此外,开发者应该重点关注数据层面的解耦。明白这一点非常重要,因为一旦数据层面存在耦合性,代码实际上就无法真正做到解耦。还有一个常见原因会导致分布式单体出现,就是过度追求复用性。这种团队或开发者通常表现为,一旦发现存在重复性的功能代码就会将其提炼到公共服务组件中。然而这种复用性在同一个上下文内是值得提倡的,但跨上下文会导致耦合加重,进而令到服务的自主性2下降。因为这么一来当前服务组件就多了一个外部依赖。一旦请求处理需要协作时,其通信成本会导致服务质量下降。所以本质上是一种违背“高内聚,低耦合”设计原则的做法。鉴于分布式设计的水平参差不齐,所以事实上业界正急需一种更加明确且规范的设计方案。而这正是微服务的价值所在。

微服务的样子

以下是两位软件大咖对微服务作出的定义:

简而言之,微服务架构风格是一种将单个应用程序开发为一套小型服务的方法,每个服务都在自己的进程中运行,并通过轻量级机制(通常是 HTTP 资源 API)进行通信。这些服务围绕业务功能构建,可通过全自动部署机制独立部署。这些服务仅需最低限度的集中管理,这些服务可能使用不同的编程语言编写并使用不同的数据存储技术。– James Lewis & Martin Fowler (2014)

当你认真去阅读这个定义后,可能会认为微服务并没有什么特别之处,它只不过是一个在策略层面给出了指导思想的分布式架构。话虽如此,但它的意义却非同凡响。因为当一种设计论述足够清晰和通用的时候,它就有了被实现的可能性和价值。这是它与SOA最根本的区别3

定义中最值得关注的地方有两个:

  • “独立部署”和“仅需最低限度的集中管理”强调了自主性2
  • “服务围绕业务功能构建”表达了它的本质。即以业务能力作为构建服务的依据,而非功能或技术

就这样吗?并非如此。随着各大公司(如 Amazon、Netflix)对微服务实践的深入,有两个足以影响微服务成败的关键概念被暴露在实践者的视野中。它们分别是领域驱动设计(DDD)康威定律4。前者用于解决“服务围绕业务功能构建”问题(可阅读另一篇关于DDD的文章来进行了解);后者则用于支撑“独立部署”和“仅需最低限度的集中管理”的实现。

康威(第一)定律:

组织在设计时,会受到其沟通结构的制约,而产出与其沟通结构相仿的设计。– Melvin E. Conway

变体:

如果有四个小组在开发一个编译器,那么就会有一个四遍式的编译器。 – Summarizing

如果有 N 个人的小组在实现一个 COBOL 编译器,那么就会有 N−1 遍式的编译器,小组中必须有人担任经理。– Raymond

康威定律描述了这么一个问题。企业/组织最终开发出来的系统,只是其内部沟通状况的反映。 譬如,当一个企业内部存在多个开发团队时;假设需要他们共同参与一款新系统的开发,那么最终做出来的成品系统,通常会包含等同于团队数量的服务组件数。 乍眼一看,只是在描述团队数和务组件数的关系。然而,并非如此。(针对例子上下文)康威定律内在的含义是,团队之间有着天然的“沟通屏障”。他们通常有各自的处事方式。所以在潜意识上,即便是共同参与项目,他们都会选择极力地维持住自己的处事方式(譬如尽可能减少对其他团队的依赖性,以避免不必要的工作麻烦)。而这种情况就是康威定律中描述的沟通结构限制(是一种实践微服务“自主性”的天然催化剂)。 此外,还可以换一个角度来理解康威定律。即“沟通越紧密,开发出来的系统其耦合性就越高”。因为越是沟通紧密,就越容易依赖对方的代码实现。原因是清楚知道可以复用其他人代码;加上对耦合性不敏感的话,久而久之就会导系统中的各个部分缠绕在一起。而这正正是大部份开发团队目前所面临的困境,因为很容易就催生出分布式单体。

基于康威定律的启示,一些从业人员开始反思各种存在问题的组织划分方式(例如按软件层(前端、后端、数据库)生命周期活动(分析、设计、编码、测试、运维)来划分),然后得出了一些应对康威定律的策略。

策略
做法
接受确保架构不违背康威定律。即组织结构中有多少个参与团队,就有多少个服务组件。
逆康威调整根据设计出来的软件架构来调整组织结构。也就说,让软件架构来决定组织结构(逆康威调整),而不是由组织结构来决定软件架构(康威定律)。这其实是敏捷价值观的一种体现,即根据环境来调整工作方式。(思考:某种程度上DevOps也是一种逆康威调整。通过融合开发和运维两个原本独立的团队以解决沟通决裂问题。)

至此不难看出,要想真正实施微服务其实绝非易事。除了要有满足实施方案要求的能力、技术和资源之外,组织人员本身的观念和态度也尤其重要。特别是国内公司大多数都存在浓烈的传统管理文化,这使得敏捷难以实施,很多原本应该被调整或优化的流程都无法很好地得到解决。除此之外,大多数开发人员的水平也未能到达能够胜任微服务开发的程度(目前为止依然有不少人认为微服务只不过是技术问题。一些框架、容器、独立数据库而已)。

总而言之,个人认为微服务的难点并不在于技术层面,而在处事态度和管理文化。或者说,实施难度取决敏捷程度。

参考


  1. 大泥球:系统正处于一个“无架构”状态。 ↩︎

  2. 自主性:维护和部署独立。强调解耦比复用更有价值 ↩︎ ↩︎

  3. 微服务社区并不认同“微服务就是SOA”这种论调,因为两者截然不同。SOA专注于传统行业;透过运用ESB(企业服务总线,一个中心化组件)将各个异构系统打通以解决企业系统信息孤岛问题。而微服务则是一种面向产品的架构风格。 ↩︎

  4. 康威定律是一种社会学观察,自然状态下呈现的结果。可参考:康威定律论文正文 ↩︎

Dik Tam
作者
Dik Tam
只是喜欢写代码。