跳到主要内容

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

单体(Monolithic)和分布式

一个应用软件系统通常包含三个基本的组成部位:用户界面(GUI或Web)、服务度端(API)、数据库(状态持久化)。

单体中,三个部分会被打包成一个构件,并在同一条进行上运行。优势在于简单,具体表现在开发测试部署等活动方面(即维护成本较低)。而且性能相较于分布式也高出几个量级(无需网络通信,且能实现强一致性)。然而,单体会随着业务增长长时间迭代变得越来越复杂(得不到合理设计时甚至会成为“大泥球”1)。即使我们知道可以通过良好的设计及软件过程来避免这种情况发生,但这总是事与愿违。因为很多开发团队根本没有足够能力来实践这种它们。即便是优秀的开发团队,单体也还是会在某些时候显得乏力。例如即便只是修改一小处地方也还是需要重新构建和部署整个系统,这大大降低了系统的服务质量。而且单体是技术单一的,开发团队无法运用更好的技术来应对问题。特别是在云计算环境中,单体的价值被大打折扣。以上问题使得单体架构慢慢变成了一种“罪恶”(虽然个人并不这么认为)。所以现在经常可以见到,即便是小公司小团队,他们的项目一开始就不是基于单体来进行架构设计的。

上面提及的单体架构的问题都可以透过分布式来解决。所谓的分布式,其实就是将系统拆分成多个服务组件,且通常这些组件都会运行在各自的进程上。这样的好处在于使系统变得灵活;组件可以根据自己需要来进行扩展,亦可以采用更适合的技术来解决问题。但换来的好处并不是毫无代价的。分布式实际上比单体复杂得多,且需要投入的资源成本也远超单体。然而这一点往往会被一些缺乏架构设计经验的人忽视,最终导致“分布式单体”的出现。

分布式单体指的是仅在形态上表现为分布式,实际上组件之间存在各种高耦合性的系统架构。它最大的问题在于,即没有解决单体长时间迭代而产生的复杂性问题,还引入了分布式的缺点:性能损耗、一致性被削弱。而且以为组件之间存在较高的耦合性,所以当都改其中一个组件时,往往也需要修改其余相关的组件;这使得系统的维护成本大大增加。之所以会出现这种情况,个人认为最大的一个原因是缺乏对分布式有效性前提的认识。前面提到过,分布式实际上比单体复杂得多。它对团队的设计开发能力都有较高的要求。团队本身应该有足够的能力和资源来应对分布式开发和运维的复杂性。此外,还需要在数据层面上对组件进行解耦。明白这一点非常重要,因为数据层存在耦合,代码实际上就很难做到真正的解耦。其次,还有另一个常见的原因会导致分布式单体的出现,就是过度追求复用性。譬如一些团队只要发现重复性的功能代码就会将其抽取到公共组件中去。这种行为在单体架构中是值得提倡的,然而在分布式环境下就需要斟酌了。因为这么一来,当前组件就多了一次外部请求;性能上产生损耗之余,还加大了组件之间的耦合性;是一种违背“高内聚,低耦合”设计原则的做法。所以,事实上分布式急需一种更为明确的指导思想来规范其实现。而这正是微服务的价值所在。

微服务的样子

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

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

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

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

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

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

康威定律:

(团队)组织在设计系统时,受到其沟通结构的制约,只能产生与其组织沟通结构相仿的设计。– Melvin E. Conway

康威定律的变体:

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

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

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

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

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

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

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


  1. https://en.wikipedia.org/wiki/Anti-pattern#Software_engineering_anti-patterns ↩︎

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

  3. https://en.wikipedia.org/wiki/Domain-driven_design ↩︎

  4. https://en.wikipedia.org/wiki/Conway%27s_law ↩︎

  5. https://www.thoughtworks.com/insights/blog/customer-experience/inverse-conway-maneuver-product-development-teams ↩︎

  6. https://en.wikipedia.org/wiki/Information_silo ↩︎

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