千万别用微服务!
本文转载自:众成翻译
译者:贺贺呵呵
链接:http://www.zcfy.cc/article/1347
原文:http://basho.com/posts/technical/microservices-please-dont/
关于译文,本文英文题目为Microservices - Please Don't,在此我联想到多年前的一本书叫做《千万别学英语》,其本意是千万别这样学英语,本文的目的亦是如此,因而这里我将题目翻译为千万别用微服务,实则是想建议读者,千万别这样使用微服务。在此声明,避免误解~。
本文改编自2015年12月份我在波士顿Golang大会上的一个简短演讲。
一时间,仿佛全世界都在痴迷于讨论微服务(microservice)。无数的新闻扑面而来,都在吹捧着诉说微服务模式如何拯救了他们的工程架构。你甚至可能曾在这样的公司工作过,他们大肆宣传这种轻量神奇的微小服务,并极力宣传他们是如何使用微服务解决旧系统的冗余,难以维护等诟病的。
当然,就理想目标而言,没有什么比现在的技术更先进,更靠谱的了。理想的美便在于这几个月以来,我们始终在为接近完美而不断地努力着。
我将阐述一些关于微服务的主要误区和坑点,并借助于一些像上述那样,将庞大的遗留系统按照微服务的理念进行拆分的企业作为我的论据。当然,我并不是想用此文来说明微服务是不好的,而是想通过我的叙述,能够让大家在决定使用这种模式之前提出一些问题,多一些思考,再决定是否向这种模式迁移,是否能够真正的适合你们自己的项目。
到底什么是“微服务”?
目前真的没有对于微服务到底由什么组成或者不包含什么的一个完美的定义,但是已经有少数微服务的支持者,编撰处理一套比较合理的说明。
再强调一下,微服务不是一个复杂的整体。在实践过程中,微服务真正被赋予的意义在于,其仅仅能够处理尽可能有限的域空间,以便其能够尽可能做最少的事情,在整个应用栈中达到预期的服务目标。举一个具体的例子,如果你有一个需要“登录服务”的银行,你想要做的最后一步便是获得用户的交易记录。你需要将这一需求移交给“交易服务”一类的进行处理(记住,命名是一件值得商榷的事情,并不是轻而易举的)。
此外,当人们讨论微服务的时候总是在潜意识里认为服务之间是通过远程交互的。因为它们是独立的进程,而且经常运行在远离其他服务的地方,所以使用REST,或者RPC协议一类的远程协议进行通信变得十分自然。
一开始,这看起来着实很简单,我们好像只需要把像REST API之类的按照其作用域,封装成几个小的包,并使这些包之间通过网络进行通信。以我的经验来说,这里有5条大家所认为的“真理”,在我看来是有一定问题的:
微服务能够使代码简洁化
微服务能够使对具有单一目的事物进行编码变得更简单
微服务的使用能让其比一个整体系统运行的更快
微服务能够使得工程师们不用只限于工作在同一个代码库,方便解耦协作
微服务使得系统的弹性更容易掌控,通常在这里以使用Docker的方式来完成
误区 #1: 更简洁的代码
“你不需要引入一个网络边界作为借口来写更好的代码”
一个简单的事实是微服务,与其他的建模方法相同,需要更简洁,维护性更高的代码。因为微服务确实能够使得参与工程的部分相对减少,但是这就几乎等同于说你是通过抛弃一些期望的需求,来解决你所谓的困难。你并没有真正意义上的攻克难关,而只是抛弃了许多你可以选择的需求点。
一个主流的设计微服务的方式是,围绕逻辑上拥有独立域的“服务”,来设计你的内构件架构。这从一方面反映出微服务可以帮助你确保针对域开发的独立性,也有利于帮助你防止从主要业务逻辑,逐渐蔓延到其他地方。另一方面,使用这些微服务可以避免大量的网络使用,以及降低从网络中带来的潜在风险。
使用微服务的更大好处是,能够更好的体现面向服务架构(SOA)的思想,一旦你决定使用微服务架构,那么你事先一定已经做了足够的工作,进行了充分的准备,并且对你所定义的域有了非常到位的理解,以至于你能够从中准确的抽取这个域。一个经得起推敲的SOA架,应该构始于代码本身,并且随着时间推移,最终能够落实到具体应用栈的物理拓扑结构中去。
误区 #2: 微服务更简单
”分布式事务从来不会很容易“
尽管一开始觉得微服务看上去很简单,但是大部分的域(特别是在需要开发原型,制定战略以及多次重复定义域本身的一些新兴企业)对于整齐切分业务成一个一个的小方块是没有什么帮助的。更多的情况是,一个域想要正确地完成其使命,必须要从借助其他部分获得数据才可以。特别是当其需要委托外部域进行写数据任务时,这会变得尤为复杂。一旦你打破了一个域的封闭环境,并且需要在外部其他域存储更改数据,那么你在做的已经是分布式事务了(例如Sagas)。
请求多个远程服务会带来许多复杂的问题。是并行还是串行处理请求?你是否发现了所有可能在流程中任何节点上发生的潜在风险(在应用和网络层面上),以及对这些风险的评估?通常情况下,这些分布式事务有自己的风险异常处理方式,需要做许多努力以确保其不仅仅能发现异常,并且使其能够决定如何处理并修复自身的问题。
误区 #3: 微服务运行更快
“你可以通过简单地应用一些额外的法则,使得一个庞大的系统获得卓越的性能”
拆分服务是一件非常困难的事情,实际上你经常可以通过减少系统处理事务的数量,以使得独立系统获得更高的运行速度,或者减少独立系统加载的依赖等等。
但是从根本上来说,这是种非常逗比的办法。当然我不是质疑那些专注于微服务,通过代码隔离以加速服务的人,但是你要明白,这样做的同时,为了处理大量的调用,你也带来了更多的网络开销。虽然网络可以“足够快”,但是它也快不过直接调用内存中的代码。
此外,许多讲述性能优化的故事大多是完全以新的语言或技术栈描述的,而不只是讲述在微服务中构建代码的一些概念。使用新语言像Scala或Go语言(两种在微服务中比较流行的语言)去重构一个用Ruby on Rails或者Django,甚至是NodeJS写的应用,依赖于技术选型本身来提升性能。但是这些语言并不“关心”你是否将它用来构建“微”服务进程,它们之所以性能更好,原因很简单,因为它们本身的编译环境等语言特性就很出色,与你的微服务其实并没太大关系。
最后,对于处在仅需要启动时使用的应用来说,CPU、内存的性能几乎不成问题。问题主要出现在I/O上,一个额外的网络调用只能带来更多的I/O负载,反而适得其反。
误区 #4: 方便工程师解耦协作
“一群软件工程师维护一个独立的代码将可能导致“不是我的问题”的诟病”
好像从表面上看这使得使用更小规模的团队解决一个较小规模的问题变得简单,但是最终可能总会带来一些其他问题,比如解决问题的余地变小,使得最终成果物无法达到预期水准。
最繁琐的事情往往是最简单的,可是一个微小的改动都使得你不得不去运行不断增长的服务。这意味着你必须投入时间精力去为软件工程师在本地构建和维护一个能够简单地运行所有服务的系统。使用Docker可以方便地实现,但是当有变动发生时,这仍需要有人维护。
此外,微服务使得测试变得更难,因为要做一次集成测试,需要去了解与之有关的全部服务,捕获全部可能发生的错误情况等等。简单理解系统就可以花费很长的时间,有这时间还不如去继续开发系统。我不是说告诉任何工程师,理解系统是一种浪费时间的表现,我只想告诫人们在你真正需要这些东西之前,不要过早的引入过高的复杂度。
最后,这还会带来一些社会问题。存在于N多服务之间的bug,需要多个团队殚精竭虑的协作才能得以修复。同时这会使得团队缺少责任感,并讲责任推卸给其他团队。而当所有人都围绕着相同的代码开发,他们会彼此了解,反而会使得系统能够顺利发展。人们在一起解决问题时会变得更有积极性,相反他们只会像各个诸侯国的国王一样死守着自己的一亩三分地。
误区 #5: 更好的弹性
“使一整个庞杂系统变得有弹性的方式,与使用微服务一样简单”
不能说将你的服务打包成一个一个的独立单元,然后使用像Docker这样的得力工具进行水平弹性扩展是一件错误的事情。
但是,如果说只能使用微服务之类的来实现上述情况,那么就大错特错了。一个庞杂的整体程序同样也可以使用上述方法来创建。你可以将这个整体应用划分成许多逻辑簇,每一个簇仅负责整体系统的一个子集部分。举例来说,API请求,你的个人仪表板,以及你的后台服务器,可能都需要共享一个代码,但是你不需要在每一个簇上去处理全部这三个子集。
那么优点是,就像在微服务中存在的那样,你可以按照既定负载去客制化定义每个簇的范围,并根据既定负载对他们分别进行流量调整。所以在你决定使用微服务之前,你也可以用同样的方法来使你的庞杂系统变得有弹性。
我们什么时候应该使用微服务?
“当你已经准备好组织一个工程”
我偏向于使用通过讨论什么时候是采用到这种方法的正确时间的方式,来决定是否选用这个方法。(如果你已经开始使用了这种方法,那么应该讨论使用这种方法开始的方式是否正确)。
创建一个可靠的,可用的微服务的一个非常关键的步骤就是理解“域”的具体内容。如果你不能很好的理解它,或者你还在尝试去搞懂它,那么这样的微服务可能是百害而无一利的。但是如果你能够深入的理解“域”, 你便可以清晰地描述“域”的边界是什么,它依赖于什么,这样的微服务才有可能是一个正确的选择。
创建一个可靠的,可用的微服务的另一个关键的步骤是你的工作流,特别值得注意的是它们是如何与分布式事务产生关联的。如果你了解各种请求在系统中的处理方式,并且知道这些处理方式会在哪里宕掉,怎么宕掉的以及它们为什么宕掉等等,你可以开始着手去创建一个分布式模型,来处理你的请求。
同时,理解你的工作流程也是一定程度上在监控(monitoring)你的工作流,并且这在某种程度上应该成为你工程的核心部分。你需要精通系统的各个部分并且需要大量的数据来分析为什么某些部分表现欠佳,甚至是报错。如果你有一个靠谱的方法监控着整个系统的各个部分,那么当你水平扩展系统时,你就可以开始理解你的系统行为了。
最后,当你能证明项目和业务的实际价值时,转移到微服务将帮助你发展,壮大,以及获得更多的利润。虽然尝试新兴技术是件非常有趣的事情,但是许多企业到头来还是受制于成本。如果你因为一个说“整体且繁杂的系统是不可取的”博客,不得不推迟发布一个可以给企业带来红利的新功能,那么你就需要通过某些方式向企业证明为什么其不可取。有些时候这种权衡考虑是值得的,而有些时候却不尽如人意。所以如何抉择,选择正确的技术路线,将在很长一段时间使你所在的企业飞黄腾达。
结论
希望当下一次有人建议你使用微服务架构时,你能够多一些思考,想一想上述的那一系列条件。像我开头说的那样,我并不是说微服务是不可取的,相反,如果不从上述的观点进行细致的权衡,就跳入微服务的浪潮中去,难免会像一个不会冲浪的人,没有精湛的技术与清醒的头脑迎击浪潮,最终沉入大海,以失败而告终。
如果你想问我应该怎样做,我想说,我主张通过使用简洁的代码定义模块,以及切分独立服务等方式构建“内部”服务,如果到时候真正需要这么做的话。这并不是唯一的方法,更不是拯救糟糕代码的灵丹妙药。但是这将会使你比在做好准备之前,去尝试处理微服务做的更好,更快。
Sean Kelly(我们亲切地称其为"Stabby")已经从事软件工程师行业12年了。他是Basho公司“Taishi”项目的参与成员,现在在Komand公司工作,一个致力于安全服务编排,无需编码,授权给安全团队迅速进行自动化构建安全环境的自动化平台的公司。Komand可以帮助团队连接他们的工具,构建动态工作流,优化人力,加快事件的响应速度。你可以在twitter上关注他 @StabbyCutyou