编者按:微服务是现今常见的一个理想模式,然而它们可能不该有这一地位。本文作者 Sean Kelly 解读了关于微服务的5个谬误,并告诉你究竟什么样的企业才适合采用微服务架构。
有一段时间,好像人人都对微服务推崇不已。每打开一次新闻订阅,你都免不了会看见有些从来没听说过的公司大肆鼓吹微服务如何救他们的工程组织于水火之中。你所在的公司也很可能受到了微服务宣传的狂轰滥炸。这些神奇的小东西好像真的无所不能,恰恰是你公司里那个庞大而老旧的数据库的救星。
当然了,现在回过头来看,这些话简直假得没边。这种后见之明的魅力就在于,它总是相当接近于我们在几个月以前以为自己拥有的2.0眼力。
我在本文中将总结几个最主要的谬论以及微服务运动中出的各种岔子,这些素材来源于一位公司员工。他所在的公司也被微服务无所不能、趁早打散整合应用才是上策的论调席卷其中。虽然我这篇博文的本意并不是“微服务就是很差劲”,但我还是希望每一个读者看完以后都能从多角度考虑一下微服务架构对他们来说是否适合。
虽然有些支持者已经逐条编出了一套蛮合理的微服务条件,但对于微服务包括什么不包括什么,实在是没有一个明确的定义。
再回到它的名字来说,这自然不是一块庞然大物。但在实践中,它的实际含义是一种尽可能少涉及不同领域的一种微型服务,这样做的目的是只专注于其使用者设定的目标而精简步骤。举一个具体的例子吧,如果你开了一家有登录功能的银行,你最不想做的事情就应该是获得读取用户金融交易记录的权限。你会把这个功能扔进“交易服务”之类的部分里去(要记住起名字也不是件简单的事啊)。
此外,人们在提及微服务时,他们还暗指那些需要和其他服务远程交互的服务。既然它们是各不相同的进程,又经常是各自不在同一个位置,一般就需要使用REST(代表性状态转移)服务或者某种RPC(远端进程调用)协议使这些进程能够远程交互。
一开始这事看起来很简单。似乎我们只需要把这些小东西放进REST的应用程序接口之类的东西,然后它们就会在网上交互啦。然而我的经验表明,人们深信不疑的这五个“事实”其实并不是真的。
1、微服务会让代码更加整洁。
2、编写单一目的程序很简单。
3、它们比整合性的程序更快。
4、工程师们在不同代码库下工作比较简单。
5、这是搞定自动扩展的最简便方式(包括Docker在内)。
“就算没有这个网络边界,你也完全有理由把代码写得更好。”
最显而易见的事实是,要写出更整洁或者更可持续的代码,微服务或者其他建构技术堆栈的方式并不是必要条件。没错,程序涉及的方面少了,你偷懒或者不经思考就敲代码的几率也会下降。然而这个说法就像是,把人们想要的东西都下架了,犯罪几率就会下降一样。你并没有解决问题,只是排除掉了很多可能性罢了。
把代码内部建构在占有部分域的逻辑性服务上是现在的一种常见做法。这一方法化用了微服务的一个重要概念:在明确管理域的同时保持核心商业逻辑不会分散到不同部分。此外,使用这一方法也防止了网络被过度占用的情况,因此也不会有其引发的潜在错误出现。
基于其仿照了围绕微服务建立的服务导向型结构,这一方法的额外收益是一旦你决定要改用微服务方式,很多设计工作都已经提前完成了,对于域的理解也已经足够充分可以具体化了。一个严密的服务导向型架构会从代码本身出发,随时间推移向堆栈中的物理拓扑拓展。
“分散式交互永远都不会简单。”
一开始这样做看上去会很简单,但是大多数域(尤其是那些需要创建原型、依原型为中心发展并逐步重新定义域的新公司来说)都不会任你雕琢然后装进不同的小盒子里的。一个域的给定部分经常需要获取其他部分的数据以正常运作,当他们需要委派在域外写入数据的权限时,这就变得更难了。一旦突破了自己的影响范围,需要把其他部分也卷入到储存修饰数据的请求列中,就进入了分散式事务(有时也称作长事务)处理的领域。
在给定请求中牵涉多个远程设备这一步骤困难重重。你是能并行调用,还是只能逐个完成呢? 你是否了解这一连串环节中任何节点都可能会出现的可能错误(应用层面和网络层面皆有可能)? 这对于请求列来说又意味着什么呢? 分散式事务处理的每一部分都需要自我解决可能出现的问题的途径,这工作量可不小,既得了解可能出现的问题,还得决定应该如何解决这些问题。
“只要多应用一些附加规则,你就可以让整合应用的表现大大提高。”
想要去除这一谬误还是有些困难的,因为事实上减少任务和加载物数量等等之后个人系统总会变得更快。
但这一说法终究是不具备普遍性的,我不怀疑那些转向微服务的人们在服务速度提高的同时也发现了个人代码路径的孤立,但你得了解到你也是在许多调用之间加入了网络部分。互联网永远都不会像共驻代码调用的速度那么快,虽然很多时候它是“足够快”的。
此外,关于提高性能的说法中,很多实际上都完全是在吹捧技术堆栈的一种新语言,而不仅是关于在外部构建代码以运用微服务的概念。用Scala或者Go(两种微服务建构的常用工具)的语言重写Ruby on Rails, Django或者NodeJS这些老软件本来就会基于其所用技术带来性能上的提升。但是这些语言并不“在意”你是否把他们运载的进程叫做“微”服务,他们只是因为编译方式等因素而有更好的表现而已。
而且,对于初创公司的大部分应用,CPU或者内存的性能几乎从来都不是问题。问题在于产出比,而额外的网络调用只会增大你的投入产出比。
“一批在孤立代码库中工作的工程师只会招致‘不怪我’综合症。”
虽然在测试阶段,让小团队集中在小问题上看起来比较简单,但这最终会经常带来很多其他问题。你解决问题的空间会缩小,收获也会缩水。
最大的问题是,不管要做什么,就算做出最小的改变,你也得同时运行越来越多的服务。这意味着单单这种让工程师在本地运行所有程序的方法,也需要你投入精力和时间来打造和维护。像Docker这样的工具可以把这件事变得更简单,但是随着程序的改动,还是需要有人来进行维护。
此外,这也会让写入测验变得更加困难,因为编写一套合适的综合测试要求编写者了解所有给定交互会涉及到的服务、捕捉到所有可能的错误案例等等。单单是了解系统就要花掉更多的时间,而这些时间本来是可以用来改进系统的。虽然我从来都不会跟程序员说花在理解系统上的时间是无谓的,但我绝对会劝告人们不要在确定自己需要之前过早提高系统的复杂程度。
最终,它还会带来人际问题。横跨多个服务、需要多次调节的漏洞真的能让人心力交瘁,因为它需要多组工程师协调、同步努力来解决。还可能出现这种情况:人们都觉得自己不应该负责,尽可能地把问题都推给别的组。而当工程师们在同一个代码库中工作时,他们对于彼此的了解会不断深化,系统本身也会随之不断发展,他们会精诚协作解决问题,而不是各据一方互不往来。
“扩展微服务和扩展整合程序其实是一样简单的。”
不是说将服务分成离散单元然后通过Docker这样的工具来扩展不是一个平行扩展的好方法,但是要说这种方法只能用于微服务,那可就不对了。整合应用也完全可以用这种办法。你可以创建一个整合应用的逻辑集合,只用来处理流量的一个子集。比如,你的内置程序接口请求、仪表前段和背景工作服务器可以共用一个代码库,但是你不用分别解决这三个子集的问题。
这一点的好处是,在微服务中,你可以将单个集群调整到其给定的工作负载,以及单独扩展它们以响应给定工作负载的流量激增。所以,在一开始微服务引导你使用这种方法的时候,你应该知道它也完全适用于扩展整合应用的堆栈。
“在你们的整个工程师团体都准备好之后。”
让我们来复习一遍应该什么时候转而使用这一方法(或者是,如果你的公司刚开始运行,该怎么判断这是否是正确的开始方式)并以此作为结尾。
要构建一个严密的、可工作的微服务方式,最重要的那一步就是搞清楚你在哪一领域工作。如果你现在还不了解或者还在努力弄懂,微服务可能对你来说将是弊大于利。然而,如果你已经有了深刻的理解,并且知道了边界在哪里、相关性如何,那么微服务方式对你来说可能是正确的选择。
另外一件需要掌握的事情是你的工作流——具体来讲,是工作流与分散式事务如何关联。如果你知晓每一种类的请求在你的系统中运行的路径,还了解这些路径可能出错的位置、方式和原因,那么你就可以开始建立处理请求的分散式模型了。
同时对于工作流的了解也算是一种监控。关于监控的学问可比“微服务与整合应用孰优孰劣”大多了,但它仍然应该是你进行编程工作时的重点。要弄懂你系统的各个部分中为什么有的表现不佳甚至频出错误,你得有大量数据以供调遣。如果你建立了一个坚实的监控系统,你就能随着系统运作增进对其各部分的平行了解。
最终,当你能够给你的工程团队展现出真真切切的价值时,微服务就能给你带来公司成长、规模扩大以及收入增加了。虽然创造和尝试是很有趣的,但在一天工作结束的时候,对很多公司来说最重要的东西还是他们的底线在哪。如果你因为看到一篇博客说整合应用不怎么样而推迟上线一个可以给公司带来盈利的新功能,那你得好好跟公司交代一下了。有时候这些抉择是值得的,而有些时候不是。选择好自己的战场,打你该打的技术仗,这样以后你才能游刃有余。
我希望下次再有人给你推荐微服务时,你会考虑到这些新的条件和问题。如我开头所说,我写这篇文章不是为了告诉你微服务是不好的,而是说,不假思索地采用微服务会给未来的发展埋下隐患。
如果你要问我提倡哪种,我会建议先通过定义清晰的代码模块构建内部服务。如果未来有需要,再把他们分置到不同的服务中。这一方法不是唯一解,也不是解决代码混乱的灵丹妙药,然而比起在没做好充足准备之前就开始应付一大堆的微服务来说,这一方法可以让你前进得更快一些。
翻译来自:虫洞翻翻 译者ID:韩念欣