2018微服务狂热之死

微服务在过去几年成为一个非常受欢迎的话题。

“微服务狂热”就像这样:

Netflix在devops上非常棒。 Netfix做微服务。 所以:如果我做微服务,我也就非常擅长devops了。

很多情况下,我们已经付出了巨大的努力来选用微服务模式,而没有清楚应用于当前具体问题的成本和收益。

我将详细描述微服务是什么,为什么这种模式非常吸引人,还有一些目前遇到的关键挑战。

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

什么是微服务,为什么如此流行?

让我们从基础开始。 以下是一个假想的视频共享平台的实现方式,首先是一个monolith(单个单元),然后是microservices微服务的形式:

两个系统的区别在于第一种是单一模型, 宏系统。 第二种是一组小的,特定的服务集合。 每个服务都有特定的角色。

通过上述图表展示,很容易看出,微服务有很多潜在的好处:

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

独立部署:每个单独的组件都可以独立部署。这样可以以更快的速度和更少的风险释放新功能。比如对 “Streaming”组件的修复可以独立部署,而不需要部署其他组件。

独立伸缩:每个组件可以彼此独立地进行伸缩。有新剧集时,服务负载加重,可以扩容“Download”组件,以处理增加的负载,而不必扩容其他组件,这使得弹性缩放更加可行,并且降低了成本。

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

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

如果这么好,为什么之前没有这样做呢?

这个问题有两个答案。其中之一是,尽技术所能,另一个是最近的技术进步使我们能够把它提升到一个新的水平。

当我开始写这个问题的答案的时候,微服务发展是个很长的过程,所以实际上我将把它分成另一篇文章,稍后再发表(请关注作者Twitter)。在这个阶段,我将跳过从单个程序到多个程序的过程,忽略Enterprise service bus(企业服务总线)和Service Orientated Architecture(面向服务的体系结构),component design(组件设计)和BoundedContext(有界上下文,领域驱动设计概念)等等。

有兴趣的人可以分开阅读更多关于整个微服务演进的资料。相反,我会说在很多方面我们已经这样做了一段时间了,但是随着最近容器技术(特别是Docker)和orchestration technology(服务编排技术)(如Kubernetes,Mesos,Consul等等)的普及,从技术的角度来看,这种模式变得更加可行。

所以,如果我们把它看作是我们可以实施微服务安排的话,我们就需要仔细考虑这个应该。我们已经从整体看到了效益,但又有哪些挑战呢?

微服务有哪些问题?

微服务即使如此的好,那又什么大不了的?下面是思考的一些比较大的问题。

增加开发的复杂性

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

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

增加运维的复杂性

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

增加devops的复杂性

阅读以上两点,可能会感到运维和开发是分开处理的,尤其是考虑到devops作为一种实践的普及(我是一个很大的支持者)。不开发缓解这一点?

面临的挑战是,多团队仍然依靠独立的开发和运维团队来运行 , 相比来说单一团队则更倾向于采用微服务。

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

它需要更多的专业知识

当由专家完成时,结果可能是美好的。但想象一下,一个独立团队如果连monolithic(单一系统)系统都不能平滑设计和维护。又怎么能增加系统数量,从而增加运行的复杂性呢?

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

现实世界的业务往往界限不清

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

这是事情变得非常复杂的地方。如果你的边界实际上没有被很好地定义,那么会发生什么,即使理论上可以孤立地部署服务,你会发现由于服务之间的相互依赖关系,你必须将服务独立组合部署。

这意味着您需要管理协同版本,这些版本的服务在合作时经过验证和测试,您实际上没有可独立部署的系统,因为要部署新功能,您需要仔细编排许多依赖服务,并且同时部署。

服务状态的复杂性往往被忽略

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

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

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

同样,可能会想到,在NoSQL数据库中,这些架构问题会消失,但事实并非如此。 不强制执行scheme的数据库不会导致无scheme系统,相反它们只是意味着scheme往往是在应用程序级而不是用数据库级进行管理的。 理解数的模型以及如何抽象这些根本性挑战不能被消除。

沟通的复杂性往往被忽略

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

用户通过“video sharing”服务进行上传视频。我们可能需要运行“upload service”上传服务,将数据传递到“transcode service”转码服务,更新subscriptions订阅,更新recommendations建议等等。所有这些请求都需要一定程度的处理,如果请求失败,我们需要重试。

这个重试逻辑可能难以管理。试图同步做事往往会导致站不住脚,失败点太多。在这种情况下,更可靠的解决方案是使用异步模式来处理请求。这里面临的挑战是异步模式(固有地使系统具有状态性)。如前所述,分布式系统状态很难处理。

当一个微服务系统使用消息队列进行服务内通信时,你基本上有一个大的数据库(消息队列或broker代理)将这些服务粘合在一起。同样,虽然起初看起来似乎不是一个挑战,版本迭代,契约变更会很痛。 V1版本的服务可能会发送某种格式的消息,当发送服务更改其发送的消息的契约时,依赖于此消息的服务也将需要更新(>排除向下兼容模式)。

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

这再次强调,独立部署的想法可能不会像预期的那样。

版本控制可能很难

为了缓解前面提到的挑战,版本控制需要非常谨慎的管理。一种趋势,假设遵循像Semver Semantic Versioning(语义化版本)这样的标准将解决这个问题。 Semver是一个明智的使用习惯,但是仍然需要跟踪依赖的API版本才能正常工作。

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

在软件系统中管理依赖关系是非常困难的,无论是node模块,Java模块,C库还是其他。当一个实体消费独立模块之间的冲突的挑战是很难被处理的。

当依赖关系是静态的时候,这些挑战就已经很难,但可以通过patched打修补,更新,编辑等进行处理。如果依赖关系本身是在线系统,那么可能无法更新它们 ,这就可能需要运行许多版本(已经描述过这些挑战),或者直到整个系统得到修复。

分布式事务

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

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

微服务可能是变相的monoliths服务

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

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

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

微服务狂热之死!

通过仔细考虑决定避免狂热。 为了帮助解决这个问题,我已经注意到了一些你可能想问自己的问题,以及可能的答案:

问题整体PDF:

https://github.com/dwmkerr/blog/raw/master/2018/microservice-madness/images/microservice-questions.pdf

最后的想法:不要混淆微服务和架构

我故意避免这篇文章中提及架构两字。但是,我的朋友佐尔坦(Zoltan)在证明这篇文章的时候提出了一个很好的观点(他贡献了这篇文章)。

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

微服务在许多方面与包装和操作的技术过程有关,而不是系统的固有设计。部件的适当边界仍然是工程系统中最重要的挑战之一。

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

我希望你发现这篇文章有趣!

本文译自dwmkerr "The Death of Microservice Madness in 2018"

  • 发表于:
  • 原文链接:http://kuaibao.qq.com/s/20180125A0DOQ200?refer=cp_1026

扫码关注云+社区