解决与平衡分布式系统中微服务的复杂度
点击上方“服务端思维”,选择“设为星标”
回复”669“获取独家整理的精选资料集
回复”加群“加入全国服务端高端社群「后端圈」
原文标题: Untangling Microservices, or Balancing Complexity in Distributed Systems
原文地址[1]
翻译:祝坤荣
微服务的蜜月期已经结束了。Uber正在将成千记的微服务重构成一个更加可管理的方案[1];Kelsey Hightower正在预测单体架构将是未来[2];Sam Newman甚至声明微服务不应该是第一选择,而应该是最后一个选择[3]。
这是怎么回事?尽管微服务承诺了简单和灵活,为什么这么多的项目变得难以维护?或者难道最终单体架构更好?
在这篇文章中,我想要讨论这些问题。你会看到一些将微服务编程一团分布式大泥球的常见设计问题 - 当然,也会看到如何避免他们。
但最开始,让我们先了解下什么是单体架构。
单体架构
微服务一直是被认为是单体应用代码的解决方案。但是单体应用是不是一个问题呢?根据维基百科的定义[4],一个单体应用是自包含且与其他计算应用独立的。与哪些其他应用独立呢?这不是我们在设计微服务时追求的吗?David Heinemeier Hansson[5]指出了单体应用的缺陷。他
因此,微服务不是“修复”单体应用。微服务需要解决的真正问题是交付业务目标的无力。一般,团队是由于指数级增长的 - 或更糟的不可预测性 - 进行变更的成本才交付不了业务目标的。换一句话说,系统不能满足业务的需要。不可控的变更成本不是单体应用的特性,而是大泥球的特性[6]:
大泥球是杂乱的结构,无序,泥泞,缠在一起的电线和胶带,面条代码的丛林。系统显示出无节制增长,重复,临时修复的明显迹象。系统中混乱的将信息在很多极长链路的系统部分中共享,这表示大部分重要信息都变成了全局的或被重复复制的。
对大泥球的复杂性的修改和进化可以由于多个原因引起:协调众多团队的工作,非功能性需求的冲突,或一个复杂的业务域。无论怎样,我们经常试图将这种复杂问题分解成微服务来解决。
微什么?
文字“微服务”指明了服务的一部分可以被度量并且它的价值应该是最小化的。但微服务到底意味着什么?我们看下一些常见的用法。
微团队
第一个工作在服务上的团队大小。而这个尺度可以按披萨来度量。你没听错。他们说如果工作在服务上的团队可以被2个披萨喂饱, 那么这就是微服务。我发现这很有启发,我曾经做一个项目而团队可以被一个披萨喂饱... 而我敢对任何人说这团大泥球是微服务。
微代码库
另一种广泛使用的方法时基于它的代码库来设计微服务。有些人将这个概念发挥到了极致,将服务的大小限制到了某些确定的代码行数。就是说,可以构成一个微服务的确切代码行数还没被找到。当这个软件架构的圣杯被发现,我们会进入下一个问题 - 构建微服务团建的编辑器宽度是多少?
有个更严重的问题,这个方法一个没那么极端的版本更流行。代码库的大小常被用来决定它是否是一个微服务。
某些时候,这个方法管用。更小的代码库,更小的业务域。因此,这容易理解,实现,发展。而且,更小的代码库不太可能变成一个大泥球 - 如果发生了,也比较容易重构。
不幸的是,前面提到的简单只是一个错觉。当我们开始基于服务本身来评估服务的设计时,我们忽略了系统设计的核心部分。我们忘记了系统自己,服务作为系统的组成。
“有很多有用和有启发性的方法来定义一个服务的边界。大小是最不重要的部分。” - Nick Tune
我们开发系统!
我们开发系统,而不是服务的集合。我们使用基于微服务的架构来优化系统的设计,而不是设计独立的服务。无论别人怎么说,微服务不能,也永远不会完全解耦,和独立。 你不能用完全独立的组件来打造系统! 现在我们看下“系统”的定义[7]:
1.一组连接在一起并可一起操作的物件或设备2.一组为了一个特定目的一起使用的计算机设备或程序
服务会与其他服务进行不断交互来形成系统。如果你通过优化服务来设计一个系统,却忽略了他们之间的交互,最终你可能是这样的结局:
这些“微服务”可能自身很简单,但系统却变成了复杂性的地狱!
所以我们如何不只是处理了服务的复杂性,而是也考虑了整个系统的复杂性来进行微服务设计呢?
这是个困难的问题,但幸运的是,在很早以前就有答案。
系统视角的复杂性
四十年前,还没有云计算,没有全球规模的需求,不需要每11.7秒部署一次系统。但工程师仍然需要控制系统复杂度。尽管这些工具与现在不一样,但挑战 - 更重要的是, 解决方案 - 都是类似的,也可以被用于基于微服务设计的系统。
在他的书里,“组合/结构设计”[8],Glenford J. Myers讨论了如何用结构化的过程代码来降低复杂度。在书的第一页,他写到:
关于复杂性的主题中有比简单的尝试最小化程序中一部分的本地复杂度更重要的事。一个更重要的复杂度类型是全局复杂度:程序或系统的全局结构的复杂度(比如,程序主要部分的关联或独立程度)。
在我们的语境里,本地复杂度就是每个独立微服务的复杂度,而全局复杂度是整个系统的复杂度。本地复杂度依赖于一个服务的实现部分;全局复杂度是被服务间的交互和依赖所定义的。
所以哪一个复杂度更重要 - 本地还是全局?让我们看看当只有一种复杂度被关心时的情况。
要将全局复杂度降到最小实际非常简单。我们只要评估下任何系统组件间的交互 - 即,将所有功能在一个单体服务中实现。就像我们早前看到的,这个策略在某些特定场景是有用的。而在其他场景,它会导致恐怖的大泥球 - 可能是最高级别的本地复杂度。
从另一方面,我们很清楚当你只优化本地复杂度而忽视系统全局复杂度时会发生什么 - 更大的分布式大泥团。
因此,当我们只关注复杂度的某一种,选哪一个并不重要。在一个复杂分布式系统,对向的复杂度都会暴涨。所以,我们不能只优化一个。相反,我们要平衡本地和全局复杂度。
有意思的是,在“组合/结构设计”一书中描述的复杂度平衡不仅与分布式系统有关,其也提供了如何设计微服务的见解。
翻译待续 ...
本文来自祝坤荣(时序)的微信公众号「麦芽面包,id「darkjune_think」 开发者/科幻爱好者/硬核主机玩家/业余翻译。
— 本文结束 —
关注我,回复 「加群」 加入各种主题讨论群。
对「服务端思维」有期待,请在文末点个在看
喜欢这篇文章,欢迎转发、分享朋友圈