2018年微服务将疯狂至死

本文是Dave Kerr发表的一篇微服务批判性文章,他认为复杂性是导致微服务将死的一个重要原因,实际上微服务本来是解决复杂性的,将牵一动百的单体架构变成很多独立发展的服务,相互隔离,复杂性关键是因为隔离不清还是现实世界根本无法隔离分清或者没有能力去隔离?其实单体系统最大问题是单点风险,而微服务化解了单点风险,但是这种化解是依靠多点替代单点,当然这个多点粒度也不能太细,否则如果从单点逻辑角度去看微服务,会觉得相当复杂, 当然关键是切分以后如何再简单方便地拼起来。

下面是其原文的大意翻译:

微服务在过去几年成为一个非常受热门的话题。“微服务疯狂”就像这样:

Netflix在devops上非常棒,Netfix做微服务。因此:如果我做微服务,我应非常擅长devops。

很多情况下,为了采用微服务模式我们付出了巨大的努力,根本不知道其成本和收益是否适用于当前问题的具体情况。

这里我将详细描述微服务是什么,为什么这种模式非常吸引人,还有一些他们提出的关键挑战。

如果你正在考虑微服务是否是适合你的模式,我会用一系列简单的问题来结束这个问题。问题在文章的最后。


什么是微服务,为什么如此受欢迎?
让我们从基础开始。以下是一个假想的视频共享平台的实现方式,左边是一个单体的,右边是微服务的形式:

视频共享平台 vs. (微服务: 上传服务 流服务 转码服务 下载服务 推荐服务 订阅服务)

两种架构的区别在于第一种是单一的大单位,一块巨石。第二种是一组小的,具体的服务,每个服务都有特定的角色。

微服务有很多潜在的好处:

1.独立开发:小型独立组件可由小型独立团队构建。一个小组可以改变“上传”服务,而不会干扰“转码”服务,甚至不知道这个服务。了解组件的时间大大减少,开发新功能也更容易。

2.独立部署:每个单独的组件都可以独立部署。这样可以更快的速度和更少的风险释放新功能。“Streaming流服务”组件的修复或功能可以独立部署,而不需要同时部署其他组件。

3.独立的可伸缩性:每个组件可以彼此独立地进行缩放。在繁忙时段发布新的节目时,可以放大“下载”组件处理能力,以处理增加的负载,同时不必扩大其他每个组件处理能力,这使得弹性缩放更加可行并降低了成本。

4.可重用性:组件实现一个小的,特定的功能。这意味着它们可以更容易地适用于其他系统,服务或产品。“转码”组件可以被其他业务部门使用,甚至可以变成一个新的业务,也许为其他组提供转码服务。

在这个细节层面上,微服务模型比单体模型的好处是显而易见。所以如果是这样的话 - 为什么这种模式最近才流行?

如果这太好了,为什么之前没有这样做呢?
这个问题有两个答案。其中之一是,它正好是我们的技术能力应该做的,另一个是最近的技术进步,使我们能够把它带到一个新的水平。

当我开始写这个问题的答案的时候,它变成了一个很长的描述,所以实际上我将把它分成另一篇文章,稍后再发表。在这个阶段,我将跳过从单个程序到多个程序的过程,忽略ESB和面向服务的体系结构,组件设计和有界的上下文等等概念。

有兴趣的人可以分开阅读更多关于发展过程的信息。我们可以说在很多方面我们已经这样做了一段时间,但是随着最近容器技术(特别是Docker)和配合技术(如Kubernetes,Mesos,Consul等等)的普及,从技术的角度来看,这种模式变得更加可行。

所以,如果我们把它看作是我们可以实施微服务管理的话,我们就需要仔细考虑这个应该。我们已经看到了高层次的理论效益,但挑战又如何呢?

微服务有什么问题?
如果微服务是如此之伟大,那难道没有大问题吗?下面是我见过的一些最大的问题。

1. 开发人员的复杂性增加

对于开发者来说事情会变得更加困难。开发人员想要在合作的情况下,或者可能跨越许多服务的实现一个功能的情况下,开发人员必须在他们的机器上运行它们,或者连接到它们。这通常比简单地运行单个程序更复杂。

这个挑战可以通过工具得到部分缓解,但随着构成系统的服务数量的增加,开发人员在整个系统运行时面临的挑战也越来越多。

(banq注:微服务实际是每个团队负责自己的服务,跨服务开始实际就是跨团队开发,微服务前提是组织架构决定技术架构,跨团队实际是对组织架构的挑战,当然会带来复杂性,这个复杂性不是微服务本身带来的。)

2.运营的复杂性增加

对于不开发微服务但却维护微服务的团队来说,潜在的复杂性是一个巨大的挑战。他们不是管理几个正在运行的服务,而是管理数十、数百或数千个正在运行的服务。存在有更多的服务,就需要更多的沟通途径,以及更多潜在的失败领域。

(banq注:避免单点失败是实现多点,多点却带来复杂性,因此需要更强大的微服务运维监控平台)

3.devops增加了复杂性

devops能够减轻以上两点吗?

对于已经采用了devops的组织来说,这仍然很难。既是开发者又是运营者,已经非常艰难(要建立好的软件却很关键),但是也必须了解容器编排系统的细微差别,特别是掌握快速发展的系统是非常困难的。这使我想到下一点。

4.它需要严肃的专业知识

当由专家完成时,结果可能是美好的。但想象一下,如果一个单一的单体系统可能都不能顺利运行的公司。怎么可能在增加系统的数量,从而增加运行的复杂性以后能够表现得更好呢?

是的,通过有效的自动化、监控和编排等,这一切都是可能的。但挑战很少是技术 - 挑战是找到能够有效使用的人。目前这些技能需求非常高,可能很难找到。

5.现实世界的系统往往界限不清

在我们用来描述微服务的好处的所有例子中,我们谈到了独立的组件。但是在很多情况下,组件并不是独立的。在论文中,某些领域可能看起来是有边界界线,但是当你进入泥泞的细节时,你会发现他们比你预期的更具挑战性。

这是事情变得非常复杂的地方。如果你的界限实际上是没有明确定义,那么会发生什么情况是,即使理论上的服务可以单独部署,你会发现,由于服务之间的相互依存关系,您必须部署一组服务。

这意味着您需要管理协同多个运行版本,这些版本的服务只有在合作运行才能通过验证和测试,您实际上已经没有可独立部署的系统了,因为要部署新功能,您需要仔细编排许多服务以进行同时部署它们。

6.状态的复杂性往往被忽略

在前面的例子中,我提到一个功能部署可能需要同时部署多个版本的许多服务。假设合理的部署技术可以缓解这种情况,例如蓝/绿部署(大多数服务编排平台很少费力地处理),或者并行运行多个服务版本的服务,以及决定使用哪个版本的消费通道。

如果服务是无状态的,这些技术可以缓解大量的挑战。但是无状态的服务非常地容易处理。事实上,如果你有无状态的服务,那么我会倾向于考虑跳过微服务,并考虑直接使用无服务器模型(serverless)。

实际上,许多服务需要状态。前面视频共享平台的一个例子可
能是订阅服务。订阅服务的新版本可以不同形式将数据存储在订阅数据库中。如果您同时运行这服务的两个版本,则等同于一次运行两个模式的系统。如果您进行了蓝绿色部署,而其他服务依赖于新形式中的数据,则必须同时更新这些数据,并且如果订阅服务部署失败并回滚,则可能还需要使用级联回滚后果。

同样,可能会想到,在NoSQL数据库中,这些架构问题会消失,但事实并非如此。不强制执行数据结构化的数据库不会消灭数据的结构化 - 它们只是意味着数据结构化往往应该是在应用程序级而不是数据库级进行管理实现的的。理解数据的形式以及它们是如何演变等这些根本性问题挑战是不能被消除。

7.通讯的复杂性往往被忽略

当你建立一个相互依赖的大型服务网络时,可能会有很多的服务间通信。这导致了一些挑战。首先,有很多事情可能会失败。我们必须假设网络通讯是随时可能失败的,这意味着当一个服务呼叫另一个服务时,它应该至少需要重试几次。如果当一个服务可能调用很多服务时,我们最终会遇到一个复杂的情况。

用户上传视频到一个视频共享服务中。我们可能还需要运行上传服务,将数据传递到转码服务,更新推荐和订阅状态等等。所有这些调用都需要一定程度的协调,如果某个环节网络消息失败,我们则需要重试。

这个重试逻辑可能难以管理。试图使用同步方式往往不会稳定,因为同步过程中的失败点太多。在这种情况下,更可靠的解决方案是使用异步模式来处理通信。这里面临的挑战是异步模式固有地使系统具有了状态性。如前所述,分布式状态的系统很难处理。

当一个微服务系统使用消息队列进行服务内通信时,你基本上有一个大的数据库(消息队列或代理)将这些服务粘合在一起。同样,虽然起初看起来似乎不是一个挑战,但模式会回来咬你。X版本的服务可能会写入某种格式的消息,当消息发送者服务更改发送消息的细节时,依赖于该消息的消费者服务也将需要随着更新。

服务可以有处理许多不同格式消息的能力,但这很难管理。假设,在部署新版本的服务时,您可能会有两个不同版本的服务尝试处理来自同一队列的消息,甚至可能是由不同版本的发送服务发送的消息。这可能会导致复杂的边缘情况。为了避免这些边缘情况,仅允许特定版本的消息存在可能更容易,这意味着您需要将一组服务的版本作为一个整体来部署,以确保先前版本的消息被适当地排除,不会被处理。

这再次突出表明,独立部署的想法当你进入细节时可能不会像预期的那样简单了。

8.版本控制可能很难

为了缓解前面提到的挑战,版本控制需要非常谨慎的管理。再次,可以有一种趋势,假设遵循像semver这样的标准将解决这个问题。其实并不是。Semver是一个明智的使用惯例,但是您仍然需要跟踪服务和可以一起工作的API的版本。

这可能会非常迅速地变得很具有挑战性,并且可能会导致您不知道哪个版本的服务实际上可以一起正常工作。

在软件系统中管理依赖关系是非常困难的,无论是节点Node模块,Java模块,C库还是其他。独立组件之间的冲突性的挑战是很难被单一个产品处理的。

当依赖关系是静态的时候,这些挑战是处理起来比较棘手,但是还是可以进行修补,更新,编辑等,但是如果依赖关系本身是实时服务,那么您可能无法更新它们 - 您可能需要运行许多版本(已经描述过这些挑战),或者直到整个系统得到修复。

9.分布式事务

在需要跨操作事务完整性的情况下,微服务可能会非常痛苦。分布式状态很难处理,很多小的单位可能会很难进行编排事务。

试图通过使操作幂等性,提供重试机制等来避免这个问题可能是诱人的,在很多情况下这可能起作用,但是如果你只需要一个事务失败或成功,而不需要处于中间状态。解决这个问题或者在微服务模型中实现它的努力可能是非常高的。

10.微服务可能是变相的单体

是的,单独的服务和组件可能是独立部署的,但是在大多数情况下,您将不得不运行某种编排平台,比如Kubernetes。如果您使用的是托管服务,例如Google的GKE 5或Amazon的EKS 6,则会为您处理管理群集的大量复杂性。

但是,如果您要自己管理集群,那么您正在管理一个庞大而复杂的关键任务系统。尽管单个服务可能具有前面所述的所有优点,但您需要非常小心地管理群集。这个系统的部署可能很难,更新可能很难,故障转移可能很困难等等。

在许多情况下,总体收益仍然存在,但重要的是不要轻视或低估管理另一个庞大而复杂的系统的额外复杂性。托管服务可能会有所帮助,但在很多情况下,这些服务都是新兴的(例如,Amazon EKS只是在2017年底才宣布)。

微服务疯狂之死!
通过仔细考虑的慎重决定能够避免疯狂。为了帮助解决这个问题,我已经注意到了一些你可能想问自己的问题,以及答案可能表明什么:

1.队伍大小:
你的团队是围绕一张大表吗?
是,你也许不需要微服务,但是会有部署开发运维等挑战性问题(比如因为修改一行代码导致整个项目重新部署等待很长时间)。
否,微服务也许能帮助你,当你有一个大型团队或许多团队时,组件之间的强边界或隔离可能难以实现,通过将组件隔离进入服务也许会有帮助。


2.状态:
你的系统大部分无状态吗?
是,可以考虑Serverless
否,微服务会带来复杂性,不意味着不用微服务,但是注意随着系统改变可能不是很容易管理。

3.消费者
你是为单一应用或服务构建一个解决方案吗?
是,注意,如果你是为一个客户构建系统,你发现你升级功能时,也许会一次性更新很多服务,微服务也许有效,但是设计领域时要非常小心。
否,如果你为很多不同客户设计,微服务也许是一个方案,能够快速为你的新客户带来新功能。

4.依赖性
你有很多服务依赖同一个实体吗?
是,性能这时也许需要考虑,因为你存在单点性能风险,
否,微服务也许值得,如果你不依赖某个串行流,你也许能够更加方便扩展服务,实现更有效的独立。

5.专家
有容器编排devops专家吗?
是,可以使用微服务。
否,devops容器编排的学习门槛比较高。

最后的想法:不要混淆微服务和架构
我故意在这篇文章中避免“a”字。但是,我的朋友佐尔坦(Zoltan)在验证这篇文章的时候提到了一个很好的观点。

其实并没有微服务架构。微服务只是组件的另一种模式或实现,只不过是另一件事。无论是否存在于系统中,都不意味着系统的架构问题得到了解决。

微服务在许多方面与打包和运维的技术过程有关,而不是系统的固有设计。组件之间的边界划分仍然是工程系统中最重要的挑战之一。

无论您的服务是否在Docker容器中,您总是需要仔细考虑如何将系统拼在一起。这里没有正确的答案,但有很多选择。

The Death of Microservice Madness in 2018