提到微服务,服务拆分是绕不过去的话题,但是微服务不是说拆就能拆的,需要很多的前提条件。
首先,首先在基础设施层面,要有一个持续集成的平台,使得服务在拆分的过程中,功能的一致性,这种一致性不能通过人的经验来,而需要经过大量的回归测试集,并且持续的拆分,持续的演进,持续的集成,从而保证系统时刻处于可以验证交付的状态,而非闭门拆分一段时间,最终谁也不知道功能最终究竟有没有bug,因而需要另外一个月的时间专门修改bug。
其次在接入层,API和UI要动静分离,API由API网关统一的管理,这样后端无论如何拆分,可以保证对于前端来讲,统一的入口,而且可以实现拆分过程中的灰度发布,路由分发,流量切分,从而保证拆分的平滑进行。而且拆分后的微服务之间,为了高性能,是不建议每次调用都进行认证鉴权的,而是在API网关上做统一的认证鉴权,一旦进入网关,服务之间的调用就是可信的。
其三对于数据库,需要进行良好的设计,不应该有大量的联合查询,而是将数据库当成一个简单的key-value查询,复杂的联合查询通过应用层,或者通过Elasticsearch进行。如果数据库表之间耦合的非常严重,其实服务拆分是拆不出来的。
其四要做应用的无状态化,只有无状态的应用,才能横向扩展,这样拆分才有意义。
那么, 满足了服务拆分的基础设施前提之后,我们应该先拆哪个模块,后拆哪个模块呢?在什么情况下一个模块应该拆分出来呢?
切记,微服务拆分绝非一个大跃进运动,由高层发起,把一个应用拆分的七零八落的,最终大大增加运维成本,但是并不会带来收益。
满足上述基本条件后,我们还要看业务变化,才能找到拆分的最佳时机。
第一,有快速迭代的需求。
互联网产品的特点就是迭代速度快,一般一年半就能决出胜负,第一一统天下,第二被第一收购,其他死翘翘。所以快速上线,快速迭代,就是生命线,而且一旦成功就是百亿身家,所以无论付出多大运维成本,使用微服务架构都是值得的。
这也就是为什么大部分使用微服务架构的都是互联网企业,因为对于这些企业来讲收益明显。而对于很多传统的应用,半年更新一次,企业运营相对平稳,IT系统的好坏对于业务没有关键性影响,在他们眼中,微服务化改造带来的效果,还不如开发多加几次班。
第二,提交代码频繁出现大量冲突
微服务对于快速迭代的效果,首先是开发独立,如果是一单体应用,几百人开发一个模块,如果使用GIT做代码管理,则经常会遇到的事情就是代码提交冲突。
同样一个模块,你也改,他也改,几百人根本没办法沟通。所以当你想提交一个代码的时候,发现和别人提交的冲突了,于是因为你是后提交的人,你有责任去merge代码,好不容易merge成功了,等再次提交的时候,发现又冲突了,你是不是很恼火。随着团队规模越大,冲突概率越大。
所以应该拆分成不同的模块,每十个人左右维护一个模块,也即一个工程,首先代码冲突的概率小多了,而且有了冲突,一个小组一吼,基本上问题就解决了。
每个模块对外提供接口,其他依赖模块可以不用关注具体的实现细节,只需要保证接口正确就可以。
第三,小功能要积累到大版本才能上线,上线开总监级别大会
微服务对于快速迭代的效果,首先是上线独立。如果没有拆分微服务,每次上线都是一件很痛苦的事情。当你修改了一个边角的小功能,但是你不敢马上上线,因为你依赖的其他模块才开发了一半,你要等他,等他好了,也不敢马上上线,因为另一个被依赖的模块也开发了一半,当所有的模块都耦合在一起,互相依赖,谁也没办法独立上线,而是需要总监协调各个团队,大家开大会,约定一个时间点,无论大小功能,死活都要这天上线。
这种模式导致上线的时候,单次上线的需求列表非常长,这样风险比较大,可能小功能的错误会导致大功能的上线不正常,将如此长的功能,需要一点点check,非常小心,这样上线时间长,影响范围大。因而这种的迭代速度快不了,顶多一个月一次就不错了。
服务拆分后,在接口稳定的情况下,不同的模块可以独立上线。这样上线的次数增多,单次上线的需求列表变小,可以随时回滚,风险变小,时间变短,影响面小,从而迭代速度加快。
对于接口要升级部分,保证灰度,先做接口新增,而非原接口变更,当注册中心中监控到的调用情况,发现接口已经不用了,再删除。
微服务解决的问题还有高并发。互联网一个产品的特点就是在短期内要积累大量的用户,这甚至比营收和利润还重要,如果没有大量的用户基数,融资都会有问题。
因而对于并发量不大的系统,进行微服务化的驱动力差一些,如果只有不多的用户在线,多线程就能解决问题,最多做好无状态化,前面部署个负载均衡,单体应用部署多份。
第四,横向扩展流程复杂,主要业务和次要业务耦合
单体应用无状态化之后,虽然通过部署多份,可以承载一定的并发量,但是资源非常浪费。因为有的业务是需要扩容的,例如下单和支付,有的业务是不需要扩容的,例如注册。如果一起扩容,消耗的资源可能是拆分后的几倍,成本可能多出几个亿。而且由于配置复杂,在同一个工程里面,往往在配置文件中是这样组织的,这一块是这个模块的,下一块是另一个模块的,这样扩容的时候,一些边角的业务,也是需要对配置进行详细审核,否则不敢贸然扩容。
第五,熔断降级全靠if-else
在高并发场景下,我们希望一个请求如果不成功,不要占用资源,应该尽快失败,尽快返回,而且希望当一些边角的业务不正常的情况下,主要业务流程不受影响。这就需要熔断策略,也即当A调用B,而B总是不正常的时候,为了让B不要波及到A,可以对B的调用进行熔断,也即A不调用B,而是返回暂时的fallback数据,当B正常的时候,再放开熔断,进行正常的调用。
有时候为了保证核心业务流程,边角的业务流程,如评论,库存数目等,人工设置为降级的状态,也即默认不调用,将所有的资源用于大促的下单和支付流程。
如果核心业务流程和边角业务流程在同一个进程中,就需要使用大量的if-else语句,根据下发的配置来判断是否熔断或者降级,这会使得配置异常复杂,难以维护。
如果核心业务和边角业务分成两个进程,就可以使用标准的熔断降级策略,配置在某种情况下,放弃对另一个进程的调用,可以进行统一的维护。
总之,微服务拆分的过程,应该是一个由痛点驱动的,是业务真正遇到了快速迭代和高并发的问题。如果不拆分,将对于业务的发展带来影响,只有这个时候,微服务的拆分才有确定收益,增加的运维成本才值得。
(本文作者为:网易云首席架构师 刘超)