微服务是个迷人的东西,易于开发、理解、维护,可以挑选适合技术、独立部署、易于扩充等,若曾被单体(Monolithic)架构应用程序「蛊毒」过,面对各种对微服务优点的歌颂,应该会心痒难耐。只是,我们需要哪些微服务呢?开展一个全新架构?这本质上就违反微服务的精神!
大爆炸或重构
单个微服务也许是简单的,然而为了串起多个微服务,微服务架构充满许多名词,组态伺服、服务注册与发现、负载平衡、断路器、路由网关等,单个微服务宣称的易于开发、理解、维护等优点,依赖在高门坎的基础设施与架构策略之上,光是要理解就不容易,进一步地,还需考虑如何适当安插在应用程序的哪个环节,而且,真的需要用上全部的东西吗?如果打算采纳微服务的动机,是来自于对单体应用程序的想法,并且想要从无到有建立各个服务,将之组合起来,因为没有历史包袱,这应该比较简单吧?
然而,事先规画的服务真的有用吗?值得独立出来?会不会实际上只有单个服务与之沟通,也从没有过弹性扩充的需求呢?大爆炸式的(Big Bang)重写成微服务架构,就像Martin Fowler说的:能保证的就是「大爆炸」!从另一个角度来看,如果真能做出个全新架构来取代单体应用程序,单体应用程序本身在架构上,应该是没有什么大问题,微服务架构中确实充满着许多基础设施及策略考虑,不过服务之间的关系在规画上,与开发上耳熟能详的Program to interface、模块化等概念,实质上是类似的,既然单体应用程序本身在架构上没有太大问题,从中抽取服务的成本,应该会低于重写应用程序的成本,那又何必重写呢?想要大爆炸式地以微服务架构重写应用程序,以摆脱单体架构应用程序中组件、模块间的耦合等历史包袱,只会令开发者更疲于奔命而不切实际,以重构方式逐步清理,逐步抽取服务,让应用程序随着时间推移,逐渐瓦解为多个微服务,或许是更为务实的做法!
重构的策略
重构的策略之一是重构单体应用程序,使之更遵循MVC架构的分层概念,让呈现层、商务层与储存层,彼此之间都有更清晰的界线,更具体地说,就是运用接口(对象之间的协议,而不是Java中的interface)沟通、隔离变化,若架构中各层之间有着清晰界线,就会出现可抽换的模块,一块一块的模块可能就暗示着,它们是可抽取的服务,在进一步抽取时,遇到的阻力会少一点。让商务层与储存层间有着清楚的界线,有助于看清有这些商业规则,以及各自需要的储存逻辑,也就有助于数据库上的分离,让个别模块能够拥有个别的数据储存,因为在抽取成服务之后,个别服务会拥有自己的数据库,也因为如此,个别服务可以选择更适合的资料储存方案。单体架构应用程序中,在数据储存上,往往错综复杂,例如,在应用关系数据库时,若表格之间有紧密相关的操作,那么,这些表格可能属于同一个模块;若表格数量众多,而且有着复杂的操作关系,那么,数据库表格之间也需重构,否则的话,表示单一模块过于巨大,承载了过多的职责,未来,就算抽取成服务,可能也是个「巨」服务,而不是「微」服务。
在单体应用程序重构、抽取服务期间,应用程序可能会需要满足新的需求,这时,不应该直接在单体应用程序中继续加入程序代码,以避免单体应用程序继续肥大下去。在〈Refactoring a Monolith into Microservices〉(https://goo.gl/u1Et1m)中,也谈到了重构单体应用程序的几个策略,其中之一就,是停止挖掘(Stop Digging),试着将新需求实作为服务,透过路由,将新需求的请求转发给新的服务,旧的需求转发给单体应用程序,而单体应用程序与新服务之间透过胶合程序代码来沟通。
逐步抽取服务
单体应用程序中可能有许多的模块,然而不必在一开始就全部都抽取成为服务,如果单体应用程序中的模块有着清晰的界线,而且多个应用程序存在着重用该模块的需求,才来考虑将该模块抽取为服务,这可以作为微服务粒度考虑的依据之一,避免服务过大或过小,如果未来其他服务对于该服务中某模块有着重用的需求,该服务可以进一步将该模块抽取成为服务。在抽取出多个服务之后,不同的服务之间,也许会共享某些组态信息。例如,多个服务都需要邮件服务,而邮件服务的组态信息相同,在多个服务间直接复制组态是很简单的一件事,然而,这就跟复制程序代码一样,未来若组态变更,就需要记得逐一更新应用程序的组态设定。像是《Spring Microservices in Action》第三章就谈到,组态管理的重要性经常被忽略,作者甚至面对过12,000个组态档案的迁移难题。
在抽取服务之后,就要开始重视组态管理问题,跟程序代码管理类似,组态需要视需求而重构,决定采用哪种组态方式。更进一步地,也会有版本控制之类的需求,衍生出专用的组态供应者,以及对应的组态消费者。而当组态的供应与消费,需要透过网络来供给与消费时,组态供应者就会以服务器的形式实现,也就需要决定组态交换时的协议、格式等,以便组态消费者进行组态的取得、更新等。在抽取服务的过程中,单体应用程序与服务,服务与服务之间,透过API接口沟通,不见得要马上采用任何微服务基础设施方案,在既有技术方案下逐步进行服务抽取,令单体应用程序原本的客户端不察觉功能上的变化(符合重构基本精神)。而这些步骤的进行,可能相当缓慢而踏实,在〈StranglerApplication〉(https://goo.gl/cCuxwL),Martin Fowler用绞杀藤蔓逐步剥夺被寄生树生存空间,来比喻大规模重构应用程序的这个过程。
逐步套用基础设施
是否套用微服务基础设施,也是依需求而定,也许既有的技术在抽取出服务之后,就足以应付需求,那么,就不见得要用上任何基础设施方案,毕竟,每增加一个方案,就是增加一层技术上的难度,维护的门坎也会随之上升,更何况,问题并不只有在采用哪个技术方案,更多是在于采用哪个架构策略。如果需要弹性地扩充,试着采用服务注册与发现;在需要考虑服务状态不佳、避免服务连锁性瘫痪下,再加上断路器的方案;在客户端多样化,想要避免曝露个别服务细节、以横切关切点方式,对某些客户端加以控管、限制API开放与否等时,再来设置网关路由等策略。值得一提的是,〈Netflix成为第一间完全上云的大型企业〉(https://goo.gl/yywVFV)的经验值得借镜,其中,对于速度、规模,以及策略三个阶段,也有着分层式架构等类似考虑。总而言之,对于微服务的架构,不要一开始就想要用上全部的基础设施或策略,重构会是更务实的做法!
转载于:https://my.oschina.net/u/3820994/blog/3013937