原文标题: Untangling Microservices, or Balancing Complexity in Distributed Systems

原文地址
上篇地址
翻译:祝坤荣


image.png

微服务

让我们先从精确定义什么是服务和微服务来开始。

什么是服务?

根据OASIS标准 9 ,一个服务是:

通过规定好的接口提供能访问一种或多种能力的机制

规定好的接口这部分很重要。服务的接口定义了它暴露给外界的功能。根据Randy Shoup 10的说法,服务的公共接口简单来说就是任何让数据进出服务的机制。它可以是同步化的,如简单的请求/响应模型,或者异步化的,一个生产事件一个消费事件。不管怎么说,同步或异步化,公共接口只代表让数据进出一个服务。Randy也表达了服务的公共接口就跟前门是一样的。

服务是被公共接口定义的,这个定义对于定义什么服务是微服务也足够了。

什么是微服务?

如果一个服务是被它的公共接口定义的,那么 -

一个微服务是指一个用了微型公共接口的服务 - 微型前门

这条在过程式编程中被遵守的规则,如今在分布式系统领域更加有关联性。你暴露的服务越小,它的实现越简单,它的本地复杂性越小。从全局复杂度来看,更小的公共接口会在服务间产生更少的依赖和连接。

微接口的概念也解释了广泛使用的微服务不暴露数据库的实践。没有微服务可以访问另一个微服务的数据库,只能通过其提供的公共接口。为什么?因为,数据库实际是一个巨大的公共接口!只要想想你可以在一个关系数据库上能执行多少种操作。

因此,再重申下,在分布式系统中,我们通过将服务的公共接口最小化的方式来平衡局部与全局复杂度,然后服务就变成了微服务。

警告

这听起来很简单但其实不然。如果一个微服务只是有微型公共接口的服务,那我们可以直接将公共接口限制到只有一个方法。由于这个“前门”已经小的不能再小了,这应该是完美的微服务,对吗?为了解释为什么不这么做,我会使用我另一篇博文11里的一个例子:

加入我们有如下库存管理服务:
image.png

如果我们将它拆成八个服务,每个只有一个简单的公共方法,我们可以得到完美的低本地复杂度的服务:

image.png

但我们能将它们连入系统来真正管理库存吗?并不行。要形成系统,服务需要与其他服务交互并共享对于每个服务的状态。但它们不行。服务的公共接口不支持。

因此,我们要继承这个“前门”并让这些公共方法可以支持服务间的集成:
image.png

完了!如果我们通过将每个服务完全独立的方式来优化复杂度,那么解耦的是很彻底。但是,当我们将服务连入系统,全局复杂度又升高了。不只是导致系统卷入了一团乱麻;为了集成 - 继承公共接口也超出了我们原来的意图。引自Randy Shoup,除了建设了一个小“前门”,我们也建了一个巨大的“员工专用”入口!这告诉我们一个重要的观点:

一个服务有比业务方法更多的集成方法有成长为分布式大泥球的巨大可能!

因此,一个服务的公共接口可以被最小化到什么程度不只是依赖于服务本身,也取决(主要)于它在系统中是哪一部分。一个微服务何时的解耦应该同时考虑系统的全局复杂度和服务的局部复杂度。

设计服务边界

“要找到服务边界太难了... 完全没有流程图!” -Udi Dahan

上面Udi Dahan说的话对于基于微服务的系统来说也很对。设计微服务的边界很难,基本上第一次很难做对。折让设计一个合适复杂度的微服务变成了一个迭代流程。

因此,从更大的边界开始是比较安全的 - 从上下文边界开始比较合适12 - 有更多关于系统各它的业务域的知识后,再将它们解耦成微服务。这对那些包含了核心业务域的服务特别重要13

分布式系统之外的微服务

尽管微服务只是最近才被“发明”出来,你仍可以在工业界发现很多有同样设计理念的实现。这些包括:

跨功能团队

我们知道跨功能团队是最有效的。这种团队让不同专业能力的小组工作在同一个任务上。一个有效的跨功能团队能最大化团队内的交流,最小化团队外的交流。

我们的工业只是最近才发现跨功能团队,但任务组是一直存在的。其底层的原理与基于微服务的系统是一样的:在团队内高聚合,团队间低耦合。团队的“公共接口”通过需要达成任务的技能来进行最小化(如实现的细节)。

微处理

我要通过Vaughn Vernon那经典的相关主题的博文来举这个例子。在他的博客里,Vaughn描绘了一个微服务与微处理器间有趣的相似点。他讲述了处理器与微处理器间的不同:

我发现了一个通过大小规格来帮助确定一个处理器是中央处理器(CPU)还是微处理器:数据总线21的大小

微处理器的数据总线就是他的公共接口 - 它定义了可以被传给微处理器与其他组件间的数据量。对于公共接口有严格的尺寸规格来定义这个中央处理器(CPU)是不是一个微处理器。

Unix哲学

Unix哲学,或Unix方式,是一种秉承了极简主义的模块化软件开发规范和文化。22

有人可能会反驳我Unix哲学与我的情况不符,你不能用完全独立的组件来组装一个系统。难道unix程序不是完全独立,然后形成一个可工作的系统的吗?事实正相反。Unix方式几乎字面上定义了程序需要暴露的微交互操作。让我们看看Unix哲学与微服务相关的部分:

第一条原理让程序暴露一个与其功能相关的公共接口,而不是与其原始目标不想关的:

让程序只做一件事并做好。要做另一件事,写个新的而不是在老程序里加新“特性”

尽管Unix命令被认为是彼此间完全独立的,但并不是。它们之间需要通信,并且第二条原则定义了通信的接口如何设计:

预期所有程序的输出会作为其他程序的输入,尽管可能现在还不知道。不要让输出有不相关的信息。避免严格的列式或二进制输入格式。不要强制要求交互式命令有输入。

不只是通信接口被严格限制(标准输入,标准输出,标准错误),基于这个原则,在命令间的数据传输也被严格限制住了。例如,Unix命令需要暴露微-接口并永远不依赖于其他命令的实现细节。

那么Nano服务呢?

文字nanoservice经常是用来描述一个服务太小了。有人会说上面例子介绍的一个方法的服务就是nano服务。我不同意这个观点。

nano服务用用来在忽略了整体系统时描述单独服务时用的。在上面例子中,一旦我们将系统放入方程中,服务的接口就会增长。实际上,当我们比较一下原来的单服务实现与解耦后的实现,我们可以看到一旦将服务连入系统,系统从8个公开接口增长到38个。而且,每个服务公开方法的平均数量从1涨到4.75.

因此,当我们又花了服务(公共接口),数据nano服务不再成立,因为服务被迫开始增长来支持系统的用例。

这些够了吗?

不。尽管最小化服务的公共接口是一个设计微服务的好原则,它仍然只是一种探索式的方式而不能取代常识。实际上,微接口只是更加基础,且更复杂的耦合与内聚设计原则的抽象。

比如,如果两个服务有微-公开接口,它们让需要在分布式事务中协调,它们仍是互相高耦合的。

针对微-接口在解决不同类型的耦合,比如函数,开发,语义仍然是有启发的。但那就是另一篇博客的主题了。

从理论到实践

不幸的是,我们没有一个客观方式来量化局部与全局复杂度。从另一方面,我们确实有一些设计方式可以优化分布式系统的设计。

这篇文章主要的内容就是想告诉你在评估服务的公共接口是你要不停的问自己:

  • 业务的占比是多少 - 给定服务是面向集成的endpoint吗?
  • 这是在业务上不想关的endpoint吗?在不引入面向集成的endpoint的前提下你可以将它们分离成2个或更多服务吗?
  • 合并两个服务是否能消除当初为了集成原始服务而产生的endpoint?

可以用这些原则来指导你在服务边界和接口的设计。

概要

我想最后用Eliyahu Goldratt的观点来总结下。在他的书里,他经常重复下面这些句子:

"告诉我你如何度量我,我会告诉你我怎样表现" - Eliyahu Goldratt

当设计基于微服务的系统时,很重要的就是度量和优化正确的指标。为微服务代码库设计边界,则微小组的定义会更容易。所以,开发一个系统,我们要学会算账。微服务是用来设计系统的,而不是独立的服务。

回到这片的标题-“在分布式系统中解决,或平衡微服务的复杂度”。解开微服务问题的唯一办法就是平衡每个服务的局部复杂度与整个系统的全局复杂度

引用索引

  1. Gergely Orosz’s tweet on Uber
  2. Monoliths are the future
  3. Microservices guru warns devs that trendy architecture shouldn’t be the default for every app, but ‘a last resort’
  4. Monolithic Application(Wikipedia)
  5. The Majestic Monolith - DHH
  6. Big Ball of Mud(Wikipedia)
  7. Definition of a System
  8. .Composite/Structures Design - book by Glenford J. Myers
  9. .Reference Model for Service Oriented Architecture
  10. .Managing Data in Microservices - talk by Randy Shoup
  11. .Tackling Complexity in Microservices
  12. .Bounded Contexts are NOT Microservices
  13. .Revisiting the Basics of Domain-Driven Design
  14. .Implementing Domain-Driven Design - book by Vaughn Vernon
  15. .Modular Monolith: A Primer - Kamil Grzybek
  16. .A Design Methodology for Reliable Software Systems - Barbara Liskov
  17. Designing Autonomous Teams and Services
  18. Emergent Boundaries - a talk by Mathias Verraes
  19. Long Sad Story of Microservices - talk by Greg Young
  20. Principles of Design - Tim Berners-Lee
  21. Microservices and [Micro]services - Vaughn Vernon
  22. Unix Philosophy

本文来自祝坤荣(时序)的微信公众号「麦芽面包,id「darkjune_think」
开发者/科幻爱好者/硬核主机玩家/业余翻译

微博:祝坤荣

B站: https://space.bilibili.com/23...

交流Email: zhukunrong@yeah.net

转载请注明。


祝坤荣
1k 声望1.5k 粉丝

科幻影迷,书虫,硬核玩家,译者