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