微服务被认为是有害的

2020-06-08 02:52:18

我们这个行业的最新趋势是软件架构从庞大的应用程序向微服务转变,通常还会有一篇相关的博客文章解释一切都变得多么美好。Neobank Monzo最近的一篇博客文章解释说,他们已经达到了1500个微服务的疯狂数量(每个工程师大约10个微服务的比率),并详细说明了它带来的众多挑战之一(剧透:安全)。在这篇文章中,我将试图揭穿微服务的两个假定好处:模块化和可伸缩性。

很难给“微服务”这个术语下一个恰当的定义。我读到的最常见的定义是功能性:它将是一个小于正常(因而微不足道)的功能单位。这个定义是错误的,因为我们总是可以在这个兔子洞里走得更深:执行加法是功能的单位吗?我们是否应该使用独立的二进制文件来进行简单的算术运算?我们应该在哪里划清界限呢?

我读过r/编程的一个技术定义:微服务是一种名字很糟糕的封装技术。我们已经知道实现封装服务(=一个功能单元)的目的的函数、类、模块、包、库;微服务可能只是另一个,定义的区别是微服务作为独立的二进制文件运行,并且微服务之间的通信是在网络上完成的。

模块化(或关注点分离)是软件工程的重要目标,所有主流编程语言都提供了多种不同粒度的工具来实现它。然而,这是一项非常艰巨的任务,我们经常失败,从而导致最可怕的spaggheti代码库。

拥有模块化代码库的主要困难是识别产品每个部分之间的依赖关系:这与代码库无关,而纯粹是产品的一个内在方面。您可以使用的技术工具无助于识别依赖关系,只能帮助实现这些依赖关系。如果您的代码库未能通过函数和包等工具实现模块性,那么通过在其中添加网络层和二进制边界将不会奇迹般地成功:您将最终得到HTTP代码库上的意大利面。

但是,您的代码库现在必须处理网络和多个进程。它创建了一组全新的要解决的额外问题,以及要引入的潜在错误:

网络故障(或配置错误)是事实。现在,软件的一部分无法访问的可能性要大得多。

还记得您漂亮的带有断点和变量的本地调试器吗?忘了它吧,您又回到了printf样式的调试。

您的服务之间的通信不再由编程语言处理,您必须定义和实现您自己的调用约定(一些语言承认这一点并提供结构来帮助您,如Ballina)。

安全性(哪个服务可以调用哪个服务)由编程语言检查(例如,如果您使用类作为封装技术,则使用PRIVATE关键字)。微服务更难做到这一点:Monzo最初的文章非常清楚地说明了这一点。

在性能方面,与语言无关的API和网络调用显然不太理想(这可能是视频游戏行业仍然不受这种微服务趋势影响的原因)。

但在网络时代,所有的模糊都是关于可伸缩性的(可能不是出于很好的理由,但这是另一个咆哮的话题)。我们通常通过添加更多服务器、为我们的应用程序分配X台服务器来线性扩展我们的服务。将应用程序拆分成微服务提供了更细粒度的分配可能性;但我们真的需要这样做吗?我会争辩说,由于必须专门预测每项微服务的流量,我们将面临更多问题,因为应用程序的一个部分无法弥补另一个部分的不足。使用单个应用程序,如果需要,任何部分(比如用户注册)都可以使用所有分配的服务器;但现在它只能扩展到服务器群中固定的较小部分。欢迎使用多点故障体系结构。

很明显,通过使用微服务作为您的封装级别,您将失去许多由编程语言或操作系统提供的功能。那么为什么要走这条路呢?主要好处是独立性:这是允许您单独开发、测试和部署服务的唯一封装级别。它应该被视为脱离其他封装的舒适性和安全性的逃生舱口。这是“铁锈”服务编程的不安全关键字。

与Dijkstra著名的被认为有害的论文一样,本文的主要观点是微服务被过度使用,并且它们的大多数用例在技术上都有更好的编程结构。如果您出于社会原因需要这种独立性(比如让一个工程师团队独立于另一个团队),那就去做吧,但请记住,几乎所有的技术挑战(代码模块性、可伸缩性、单点故障…)。不会通过使用微服务来神奇地解决。