【编者的话】本文作者Martin Folwer是软件开发和设计领域的专家。微服务已经成为当下架构设计的热门话题,本文分析了微服务的优缺点,特别指出了当对微服务做出取舍时需要考量的各种因素,写的很细致,发人思考。
很多开发团队已经认识到 微服务架构 比单体架构更优越。但是也有其他团队感觉到这是一种消弱生产力的负担。就像任何软件架构,微服务架构同时带来成本和收益。为了能做出一个明智的选择,你必须了解这些应用并将它们运用到你特定的环境中。
微服务最大的好处是对功能模块的划分。这个重要的好处,也是很奇特的,因为理论上没有理由来解释为何微服务比单体架构有更强大的模块划分。
那到底什么是我说的模块边界。我想大多数人同意,软件划分成多个模块,这很好:彼此就可以分离解耦。如果我需要改变系统的一小部分,大多数时候,我只需要弄清楚这一小块,然后做些改动,会发现小块变动真的很容易。好的模块化结构在任何程序中都是有用的,而且当软件的规模变大,这显得格外重要。也许,会更重要,因为团队的规模也在增加。
提倡微服务,需要快速介绍一下 康威定律 ,认为一个软件系统的结构反映了团队的沟通结构。对于较大的团队,特别对于那些团队,位于不同的地方,对软件实施结构化显得尤为重要,跨团队之间的沟通,和一个团队内的沟通相比,将会变得不那么频繁且更加正式。微服务允许每个团队用类似的沟通模式来照看相对独立的模块单元。
正如我前面所说,没有理由可以解释为何一个单体系统不应该拥有一个良好的模块化结构[1]。但是很多人都观察到这似乎很少见,因此大杂烩成了最常见的架构模式。事实上对单体架构这种普遍趋势的困惑,驱动了一些团队开始使用微服务架构。模块的解耦能够起作用,是因为模块的边界是模块之间引用的障碍。麻烦的是,一个单体系统,通常都很容易绕过障碍。这样做,可能是构建功能时有用的技术捷径,可是呢,如果广泛运用这种方法,将削弱模块化的结构和降低团队的生产力。将模块成为独立的服务,使模块的边界更为牢固,使它更难找到这些可怕的技术捷径。
耦合的重要部分是持久化的数据。微服务的关键特征是 去中心的数据管理 ,也就是说每个服务管理它自己的数据库,其他的服务必须通过该服务的API来交互。这会消除 集成数据库 ,这是大系统中令人讨厌的耦合的主要来源。
值得强调的是在一个单体系统中,使用模块化完全是可能的,但是它需要纪律来保证。同样地,你也可能得到微服务的大杂烩模式,但是在本身已是微服务的架构中,要犯这样的错还是不太容易的。在我看来,使用微服务,可以帮助你实现更好的模块化。如果你对团队纪律很有信心,那么可能会消除这一优势,不过,当团队的规模增加,这将越来越难保持纪律,也就意味着维护模块边界的重要性。
如果你不好好管理模块的边界,这个优势就会变成障碍。这就是 单体优先策略 的两个主要原因之一,也是为何我更倾向于 较早使用微服务 的压力所在,你只能在一个领域了解得非常透彻。
但我现在不会做这一点警告。只有经过时间的洗礼,你才能真正讲出要如何才能把一个系统的模块化维护好。所以只有我们看到微服务系统已经运行好多年,才可以评估它是否会带来更好的模块化特性。而且,早期接纳者更有才华,也就是说在我们能够评估一般团队构建的微服务系统的模块化优势之前,还有好几年的延迟。所以与其和高水平的团队比较成果,还不如比较那些已经使用单体架构的软件带来的变化,这真是一个棘手的违背事实的评估。
所有我现在能进行的,就是听听那些我认识的人已经使用微服务架构的早期证据。他们的判断就是微服务更容易维护他们的功能模块。
有一个案例特别有意思。有一个团队做了一个错误的决定,在一个不那么复杂的系统上面 使用微服务 。这个项目碰到了问题,需要帮助,所以很多人跑了进来。这个点上,微服务还是有所帮助的,因为这个系统能够吸收开发者的快速涌入,和单体架构相比,团队更容易扩张。最终这个项目的进度大大超过使用单体架构的预期,使整个团队赶上进度。当然也有负面的,和单体架构相比,微服务的方式耗费了更多的人力,但是微服务的架构能够支持未来更大的规模。
如果你想要知道更多有关于如何构建微服务系统,Sam Newman的 这本书 是很重要的资料。
因此,微服务采用分布式系统来提高模块化。但是分布式软件有一个主要的缺陷,就是分布式系统这个事实。一旦你开始玩分布式系统,你就会碰到一堆复杂的问题。 我不认为微服务社区,对分布式系统所带来的成本没有概念 ,但是复杂性也确实存在。
首先是性能。这时候,你不得不以一种不常见的方式,看着进程内的函数调用转变成性能的瓶颈,远程调用是很慢的。如果你的服务调用很多远程服务,这些远程服务本身也要调用另外一些远程服务,这些响应时间加起来,就会带来很恐怖的延迟特征。
当然你能够减轻这些问题。首先,你可以增加调用的粒度,减少调用的数目。这会使你的编程模式变得复杂,现在你必须想清楚如何批量处理跨服务交互。由于你必须调用至少一次这些所有合作的服务,因此到目前为止,你能做的就这么多了。
第二种方法就是使用异步通信。如果六个服务异步并行调用,延迟只会是那个最慢的调用,而不是所有调用延迟的总和。这大大改善了性能,但也带来了认知成本。异步编程很难,很难用好它,而且很难调试。但我听到的大多数微服务的故事,需要异步来获得可接受的性能。
速度之后就是可靠性的问题。你期望中函数调用能够成功,可是一个远程调用可能在任何时间会失败。有很多的微服务,甚至还有更多的潜在的故障点。明智的开发人员知道这些, 为可能发生的故障事先设计 。你为异步协作做的方案,也适应于故障处理,还可提高服务的弹性。然而,这些补偿仍然不够,仍然有着额外的复杂性,你需要弄明白每一个远程调用失败的后果。
这就是分布式计算最主要的 两个难题 。
对于这个问题,还有一些要注意的地方。首先,很多这类的问题出现在单体架构规模扩充时。很少的单体架构可以真正独立运行,通常还是有些其他系统,遗留的系统,需要打交道。和这些系统通过网络进行交互,同样会碰到这些类似的问题。这就是为何很多人都倾向于更快转移到微服务架构,来处理远程系统的交互问题。这个问题,同样也和经验有关系,更熟练的团队更有能力处理分布式特性所带来的问题。
当然,分布式特性,永远都是一个成本。我总是不太愿意打分布式这张牌,因为想到很多很多人,会低估这些难题,而太快地引入了分布式特性。
我想你知道的,网站的更新着实需要一点耐心。更新某一个东西,刷新你的屏幕,可是更新的东西还没有出现。你等了一两分钟后,它出现了。
这是一个非常恼人的可用性问题,几乎可以肯定是由于最终的一致性造成的。你的更新被节点P收到,可是呢,你的请求却被另一个节点G处理。直到节点G从节点P那儿得到更新之前,你一直处于数据不一致的状态。最终,它会变成一致的,但在这之前,你会疑惑是不是有什么东西弄错了。
像这样的不一致是令人恼火的,但他们可以更严重。业务逻辑会停滞在对不一致的信息上做出决策,当这种事发生时,在不一致的窗口关闭之前,也难以诊断出到底什么出了问题。
微服务带来了最终一致性的问题,是因为他们对去中心的数据管理的坚持,这种坚持值得称赞。单体架构,你可以一次更新一堆东西。微服务则需要多个资源的更新,和分布式的处理,这确实头疼。所以现在,开发者需要意识到一致性问题,在写任何代码之前,弄明白如何检测数据的不一致。
单体架构在这些问题上同样不能全身而退。随着系统的增长,有更多的需要使用缓存来提高性能,缓存失效是 另一个困难的问题 。大多数应用程序需要脱机锁,以避免长期的数据库事务处理。外部系统需要更新,不能与事务管理器协调。商业流程往往更具宽容的不一致性,因为企业对可用性要求更高(业务流程,一直以来,是 CAP理论 直觉上的一种理解)。
和其他分布式问题一样,单体架构也不能完全避免非一致性问题,但它们受到的困扰不多,特别当它们规模很小时。
> 微服务是DevOps革命后第一新的软件架构
-- Neal Ford
模块化和分布式系统的复杂性一直伴随着我整个职业生涯中。但是有一件事情发生了明显的变化,就是在过去的十年中,有关生产环境的发布。在二十世纪,生产环境的发布几乎总是痛苦且罕见的事情,周末的白天或晚上部署一些能用的软件。但是现在呢,熟练的团队频繁发布到生产环境,许多团队实行 持续交付 ,使他们一天能够在生产环境发布很多次。
这种转变已经对软件产业产生深远的影响,并与微服务运动深深交织在一起。当部分的小变化可能导致整个部署失败,微服务终于被单体架构的部署难题所激发出来。微服务的一个关键原则是, 每个服务都是系统的一个组件 均可独立部署。所以现在当你做出改变时,你只需要测试和部署一个小服务。如果你把它搞砸了,但不会把整个系统都搞砸。毕竟,事先对故障进行了设计,即使是失败,你的组件也不应该停止其他部分的系统工作,尽管功能上有些退化。
这种关系是双向的。由于许多微服务需要频繁部署,统一部署行为显得尤为重要。这就是为什么应用的快速部署和快速配置是 微服务的先决条件 。对于任何以此为基础的服务,你都需要做持续的交付。
持续交付的好处是减少了由想法变成软件的时间。那么,团队可以快速响应市场变化,并快于竞争对手先引入新的功能。
尽管许多人认为持续交付是使用微服务的一个原因,但值得注意的是,即使单体架构也可以持续交付。Facebook和Etsy是两个最好的例子。还有一些尝试微服务的例子,因为多个服务需要认真协调才能发布,而因此无法独立部署[2]。同时我也听到很多人认为使用微服务能更容易做持续交付,我是不太相信模块化可以使持续发布更容易,尽管它可以大大提高交付的速度。
能够迅速部署独立的小单位,是项目开发的一大福音,但由于几十个应用现在转变为几百个小的微服务,给运维额外增加了负担。许多组织会发现处理这样一个迅速变化的工具,是一种令人望而却步的难度。
这加强了持续交付的重要性。而持续交付是单体架构一项有价值的技能,也是一个微服务所必须的。如果没有自动化协作,持续交付也无法处理了那么多的服务。运维的复杂性也由于管理这些服务和监控的需求的增加而增加。运维的复杂性对单体架构的应用也有所帮助,不过对微服务来说,这是必然的。
微服务的支持者指出,由于每个服务更小,所以更容易理解。但风险在于,复杂性并没有消除,它只是转移到服务之间的相互联系中。这会增加运维的复杂性,比如调试、跨服务的难度。良好的服务边界的选择将减少这个问题,但如果使错地方,则更糟糕。
处理这种运维复杂度,需要一个新的技能和工具 - 最大的重点是技能。工具仍然是不成熟的,但我的直觉告诉我,即使有更好的工具,微服务还得靠高技能。
然而,技能和工具,不是最难的部分,来解决这些运维的复杂性。想要有效地解决,你还需要引入一个DevOps文化:开发者和运维之间的紧密合作,每个人都参与软件交付。文化变革是困难的,尤其是在更大和更老的组织中。如果你不改变这个技能和文化,你单体架构的应用只会受到阻碍,而微服务应用则会受到创伤。
由于每个微服务是一个独立的部署单元,你有相当的自由选择需要的技术。微服务可以用不同的语言,使用不同的库,并使用不同的数据存储方式。这使得团队可以选择合适的工具来工作,有些语言和库更适合某些类型的问题。
技术多样性通常以最佳的工具为中心进行讨论,但往往微服务最大的好处是更头疼的版本问题。在单体架构中你可以只使用一个单一的版本库,这种情况经常导致升级出现问题。系统的一部分可能需要升级以使用它的新功能,但不能因为升级中断系统的另一部分。处理库的版本问题,是其中的一个难题,因为代码库会变得更大。
这里有一个风险,有这么多的技术多样性,开发团队会被压倒。我所认识的大多数组织都鼓励有限的一组技术。这种鼓励是通过提供共同的工具来支持监测,使它更容易将服务,稳定在一个小的通用环境中。
不要低估了支持使用新技术的价值。与单体架构系统,早期对语言和框架的决定是很难逆转的。经过十年左右,这些的决定可能会限制团队陷入尴尬的技术境地。微服务让团队尝试新工具,并逐步一次迁移系统的一个服务,使新老技术有所关联。
在我看来,上面的因素是作为首要权衡指标来考虑的。这里要讨论的一些事情上,我认为就没那么重要了。
微服务的支持者们经常说服务更容易扩展:一个服务得到大量的负载,你就可以扩展,而不用对整个应用进行扩展。然而,我努力回忆起一个过往的经验报告,它使我相信,和 千篇一律的应用复制 相比,它实际上是更有效地进行选择性扩展。
微服务允许你隔离敏感数据以及给数据增加安全性。此外,在保证微服务间交互安全的前提下,微服务将难以被攻入。安全问题越来越重要,这可以成为使用微服务的主要考虑因素。即使不用微服务,单体架构的系统创建隔离服务来处理敏感数据,也是很常见的。
批评者说微服务应用的测试要比单体架构的应用难度更大。虽然这个真正的困难部分来自于分布式应用程序的复杂性,但还是有 好的方法可以测试微服务 。在这里,最重要的是要有纪律,要认真测试,单体架构和微服务应用的测试方法的区别只是第二位的。
任何有关架构的综述文章都会受到 一般性建议的限制 。所以读一篇这样的文章不能帮助你做决定,但这样的文章可以帮助你确保考虑到你应该考虑的各种因素。这里对不同的系统,成本和收益将有不同的权重,甚至成本收益会被颠倒过来(在更复杂的系统中强模块化更好,但对于一个简单的系统,这就增加了障碍),你所做的任何决定取决于环境中使用条件,评估哪些因素最重要的,以及它们如何影响你的特定评估条件。此外,我们对微服务架构经验相对有限。在一个系统成熟后,你通常只会在这个体系中决定相应的架构。我们还没有很多关于长期运行的微服务架构的实例。
单体架构和微服务并不是简单的二选一。两者都是模糊的定义,意味着大多数系统都将在一个模糊的边界区域。当然也还有其他系统,不适合这两个类别的。大多数人,包括我自己,讨论微服务时用单体架构作对比,这是因为它们更常见,但我们必须记住,还是有系统不属于这两类的。我认为,单体架构和微服务是架构设计领域重要的两部分。它们值得被讨论,因为它们存在有趣的特性,以及有用的讨论,但是没有合适的架构师可以对它们在架构设计领域进行一个全面的区分。
总结起来,微服务的好处:微服务提高了生产效率,但同时也带来了复杂性。所以如果你可以用单体架构管理好你的系统,那么就无需微服务。
对微服务的讨论不应该让我们忘记了更重要的问题,驱动软件项目成功和失败的重要因素。软因素如团队中人的素质,以及他们如何彼此合作,与领域专家的沟通,这将有更大的影响,无论是否使用微服务。在纯技术层面上来讲,更重要的是把重点放在干净的代码,完善的测试,并持续关注架构的演化进步。
[1] 有些人认为“单体架构“是一种侮辱,总是意味着很糟糕的模块化结构。在微服务世界,大多数人不这样做,他们将单体架构定义为将整个应用构建为一个独立的单元。当然,微服务倡导者相信大多数单体架构最终会变成大杂烩,但我不知道有谁会认为建立一个结构良好的单体架构毫无可能。
[2] 能独立部署服务是 微服务定义的一部分 。所以这么说还是有道理的,即必须在其部署时协调服务的架构不是微服务架构。也可以这么说,很多团队尝试微服务架构而陷入麻烦是因为他们最终不得不协调多个服务来部署。
Brian Mason, Chris Ford, Rebecca Parsons, Rob Miles, Scott Robinson, Stefan Tilkov, Steven Lowe, 以及Unmesh Joshi和我一起讨论这篇文章的草稿。
原文链接: Microservice Trade-Offs
===================================
译者介绍
Henry Huang,目前供职于趋势科技 Trend Micro(南京),负责集群运维的工作。