什么时候微服务是一个坏主意? - semaphoreci


微服务听起来很棒,它们是模块化的、可扩展的和容错的。很多公司都使用这种模型取得了巨大的成功,因此微服务可能自然会成为卓越的架构和启动新应用程序的最佳方式。
然而,大多数在微服务方面取得成功的公司并不是从它们开始的。考虑一下 Airbnb 和 Twitter 的例子,它们在超越了单体应用之后走上了微服务路线,现在正在与它的复杂性作斗争。即使是使用微服务的成功公司似乎仍在寻找使它们发挥作用的最佳方法。很明显,微服务需要权衡取舍。
从单体架构迁移到微服务也不是一项简单的任务,创建未经测试的产品作为新的微服务更加复杂。只有在评估了替代路径之后,才应该认真考虑微服务。

微服务只适用于成熟产品
关于从微服务设计开始的话题,Martin Fowler 观察到
1. 几乎所有成功的微服务故事都是从一个太大而被分解的单体开始的。2. 几乎所有从零开始构建为微服务系统的系统都以严重的问题告终。这种模式导致许多人争辩说,即使您确定您的应用程序将足够大以使其值得,也不应该使用微服务开始一个新项目。
第一个设计很少完全优化。任何新产品的前几次迭代都用于寻找用户真正需要的东西。因此,成功取决于保持敏捷并能够快速改进、重新设计和重构。在这方面,微服务显然不如单体应用。如果您没有确定最初的设计,那么您将处于一个艰难的开始,因为重构微服务比重构单体应用要困难得多。

您是初创公司还是正在从事一个新项目?
作为一家初创公司(在这个经济体中不太可能),你已经在争分夺秒,在下一个坏事发生之前寻找突破口。此时您不需要可扩展性(可能几年内也不需要),那么为什么要使用复杂的架构模型来忽略您的客户呢?
在从事新建项目时可以提出类似的论点,这些项目不受早期工作的限制,因此没有任何决策依据。《构建微服务:设计细粒度系统》一书的作者 Sam Newman表示,使用微服务构建新项目非常困难:
我仍然坚信,划分现有的“棕地”系统比预先使用新的绿地系统要容易得多。你有更多的工作要做。您有可以检查的代码,可以与使用和维护系统的人交谈。你也知道“好”是什么样子——你有一个可以改变的工作系统,让你更容易知道你什么时候可能出了问题或在你的决策过程中过于激进。

微服务不是本地部署的最佳选择
由于所有移动部件,微服务部署需要强大的自动化。在正常情况下,我们可以依靠持续部署管道来完成工作——开发人员部署微服务,客户只需在线使用应用程序。
这不适用于本地应用程序,开发人员在其中发布一个包,并由客户自行在其私有系统上手动部署和配置所有内容。微服务使所有这些任务变得特别具有挑战性,因此这是一个与微服务架构不太匹配的发布模型。
需要明确的是,开发本地微服务应用程序是完全可行的。Semaphore 正是通过Semaphore On-Premise实现的。然而,正如我们一路上意识到的那样,有几个挑战需要克服。在决定采用本地微服务之前,请考虑以下事项:

  • 本地微服务的版本控制规则更加严格。您必须跟踪参与发布的每个单独的微服务。
  • 您必须进行彻底的集成和端到端测试,因为您无法在生产环境中进行测试。
  • 如果不直接访问生产环境,对微服务应用程序进行故障排除要困难得多。

你的单体可能还有生命
每个软件都有一个生命周期。你可能会想废弃一个单体,因为它很旧并且有它的复杂性。但是折腾一个工作产品是浪费的。稍加努力,您就可以从您当前的系统中挤出更多的好年头。
有两个时候,微服务似乎是唯一的前进方向:

  • 纠结的代码库:很难在不破坏其他功能的情况下进行更改和添加功能。

性能:你在扩展单体应用时遇到了麻烦。

模块化单体
开发人员想要避免单体应用的一个常见原因是他们倾向于恶化成一堆乱七八糟的代码。当我们到达这一点时,添加新功能是一项挑战,因为一切都是相互关联的。
但是单体不一定是一团糟。以 Shopify 为例:他们的代码行数超过 300 万行,是世界上最大的 Rails 单体应用之一。在某一时刻,系统变得如此之大,以至于给开发人员带来了极大的悲痛
该应用程序非常脆弱,新代码会产生意想不到的影响。进行看似无害的更改可能会引发一连串无关的测试失败。​例如,如果计算运费的代码被调用到计算税率的代码中,那么更改我们计算税率的方式可能会影响运费计算的结果,但原因可能并不明显。这是高耦合和缺乏边界的结果,这也导致测试难以编写,并且在 CI 上运行非常缓慢。
Shopify 没有将整个单体架构重写为微服务,而是选择了模块化作为解决方案。

模块化有助于设计更好的单体和微服务。如果没有仔细定义的模块,我们要么陷入传统的分层单体(大泥球),或者更糟糕的是,作为一个分布式单体,它结合了单体和微服务的最差特性。

模块化是很多工作,这是真的。但它也增加了很多价值,因为它使开发更加直接。新开发人员在开始进行更改之前不必了解整个应用程序。他们一次只需要熟悉一个模块。模块化使大型整体感觉很小。

模块化是过渡到微服务之前的必要步骤,它可能是比微服务更好的解决方案。与微服务一样,模块化单体架构通过将代码拆分为独立的模块来解决纠结的代码库问题。与通过网络进行通信的微服务不同,单体应用程序中的模块通过内部 API 调用进行通信。

分层与模块化单体。模块化单体具有许多微服务架构的特征,没有最困难的挑战。

单体可以扩展
关于单体的另一个误解是它们无法扩展。如果您遇到性能问题并认为微服务是唯一的出路,请三思。Shopify 向我们展示了声音工程可以以令人难以置信的规模使工作成为一个整体:
架构和技术栈将决定如何优化单体;一个几乎总是从模块化开始并可以利用云技术进行扩展的过程:

  • 部署单体应用的多个实例并使用负载平衡来分配流量。
  • 使用 CDN 分发静态资产和前端代码。
  • 使用缓存来减少数据库的负载。
  • 使用边缘计算或无服务器功能实现高需求功能。

如果它正在工作,请不要修复它
如果我们将生产力衡量为随着时间的推移实现的增值功能的数量,那么当生产力很强时,切换架构几乎没有意义。

由于维护开销,微服务最初是生产力较低的架构。随着单体的增长,它变得更加复杂,并且更难添加新功能。微服务只有在线路交叉后才能得到回报。
诚然,有些事情最终必须改变。但这可能是几年后的事,到那时,需求可能已经发生了变化——谁知道同时会出现哪些新的架构模型呢?

布鲁克定律和开发人员生产力
The Mythical Man Month (1975) 中,Fred Brook Jr 表示“为后期的软件项目增加人力会使它变得更晚”。发生这种情况是因为必须先对新开发人员进行指导,然后才能处理复杂的代码库。此外,随着团队的壮大,沟通开销也会增加。组织起来和做出决定变得更加困难。

适用于复杂软件开发的布鲁克定律指出,在后期的软件项目中增加更多的开发人员只会使其花费更长的时间。
微服务是减少布鲁克定律影响的一种方法。然而,这种效果只在复杂而庞大的代码库中才能看到,因为在这种情况下,我们无法将开发划分为离散的任务。
在决定使用微服务之前,您必须确定布鲁克定律是否正在影响您的单体应用。过早切换到微服务不会增加太多价值。

你准备好过渡了吗?
在开始使用微服务之前,必须满足一些条件。除了准备单体之外,您还需要:


改变组织的文化可能需要数年时间。学习所有知识需要几个月的时间。如果没有准备,过渡到微服务是不可能成功的。

结论
我们可以用一句话总结关于过渡到微服务的整个讨论:除非你有充分的理由,否则不要这样做。那些毫无准备且没有可靠设计就踏上微服务之旅的公司将度过一段非常艰难的时期。在微服务被视为一种选择之前,您需要达到一定数量的工程文化和扩展专有技术。
同时,如果您的系统运行良好并且您仍在以不错的速度开发功能,为什么要进行更改?


banq​注:本文只以shopify一个案例为例,不具有普遍性,微服务已经是大部分国外互联网大厂默认选项,况且shopify​引入了DDD:全球大型电商Shopify如何使用DDD实现单体架构的模块化?

banq个人认为:Martin Fowler的关于“先单体后微服务”观点是错误,这会对程序员水平很高,他们能站在整个单体系统像上帝一样俯视整个系统,然后像上帝一样开始小心翼翼重构切分
当然,在系统中小型规模时,团队还是有一些强人特别时源作者,能对整个系统认知极其深入,代码几乎都是他一个人编写出来的。
但是,还有一种认知误区​:身在庐山中,不识真面貌,由于太过于细节和付出,能够拿起刀来用于切蛋糕很难,况且如何切,也不是根据经验的,因为上下文切分,微服务划分是依据业务规则和流程中的上下文,但是业务规则和流程也不是不变的,​涉及到产品领域的不确定性。
微服务不只是代码的切分,而且是写代码的程序员团队的切分与合作,如果一开始使用单体,根据康威定律(组织架构决定了软件架构),单体架构的团队容易导致权威政治HiPPO效应​,日后很难转型为微服务架构下的分享工作,分享决策的民主模型。