分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。
分类: Java
2021-03-29 16:03:31
随着业务系统的复杂性越来越高,系统之间的调用也越来越多,在微服务拆分和迭代过程中,是不断的拆分出新的独立的服务还是封装独立的组件以jar包依赖的方式提供服务是我们经常需要面对的问题,本文将详细探讨这两种不同的方式区别、各自的优劣势及适用的场景,希望能够对大家有所启发。
随着公司互联网业务发展越来越迅速,系统的复杂性越来越高,系统之间的调用也越来越多,在微服务拆分和迭代过程中,经常会遇到两种场景问题:
自己系统内部的一些公共功能模块到底以什么形式提供服务,是封装好所有逻辑和方法然后以jar包的形式提供,还是独立拆分出一个服务然后通过接口的方式来提供;
涉及对外部系统服务的调用,系统内部可能有很多的服务都需要调用外部服务,但是调用逻辑都是一样的,那么针对外部系统的接口调用逻辑和方法是封装成一个jar包给内部各个服务依赖调用,还是把对外部系统的接口调用逻辑和方法独立拆分成一个服务,然后内部各服务通过调用这个独立拆分出来的服务去调用外部系统服务。
针对以上两种场景,我们可以总结概括为组件化和服务化两种不同的服务提供形式:
组件化定义:即把系统内部的一些公共功能模块或者对外部系统调用的一些逻辑方法封装成一个独立jar包,有需要的系统直接依赖该jar包来使用相应的服务,在此我们称之为组件化;
系统内部公共功能模块组件化示例,服务A、B、C都独立依赖的组件D来使用相关功能。
外部系统服务接口调用组件化示例,服务A、B、C都通过组件D去调用外部服务E
服务化定义:即把系统内部的一些公共功能模块或者对外部系统调用的一些逻辑方法独立拆分为一个服务,该服务再对外暴露统一的接口供所有有需要的服务去调用,在此我们称之为服务化。
系统内部公共功能模块服务化示例,对应就是把示例图1中组件D独立拆分为服务D,服务D再提供接口给服务A、B、C去调用。
外部系统服务接口调用服务化示例,对应的就是把示例图2中的组件D独立拆分为服务D,服务A、B、C通过调用服务D去调用外部系统服务E。
那么在实际工作中,面对不同的场景和问题,我们具体该选择哪一种方式呢?是否相关的参考标准,有哪些问题是需要我们特别关注和考虑的,接下来我们会详细介绍下组件化和服务化各自的优劣势及适用的场景。
组件化这种通过jar依赖的方式去调用第三方服务到底存在哪些优势和劣势呢?
服务调用性能高,因为都是直接通过调用jar包里的方法来调第三方服务,性能损耗较少,对性能要求较高的场景使用该方式会有一定优势;
节省服务器机器成本,因为不需要独立部署服务,可以节省服务器资源,尤其在服务器请求量大QPS高需要部署大量服务器资源的场景下能够节省的服务器资源也越多。
可维护性较差,一旦调用其他服务的逻辑方法需要变动,或者第三方提供的jar包需要升级的话,除了该组件本身需要维护升级,我们自己系统内部依赖了该组件的服务都需要跟着一起升级,随着时间的推移,梳理维护起来会很麻烦;
组件升级成本高且风险较大,系统内部依赖了该组件的服务如果很多的话升级成本会很高,这里面的成本包括了开发维护升级各个服务的成本、测试验证的成本及运维发布的成本,需升级维护的服务越多,成本越高,对应的风险也越大。
那么具体哪些场景适合使用组件化的方式来部署呢?根据我们的经验来看,符合以下场景特点的建议使用组件化的方式:
自己系统内部一些公共功能处理场景,不涉及到数据库资源层面的连接和调用,适合组件化的方式;
对外部系统服务调用场景,服务并发量大对服务性能的要求很高,主要是一些to C的服务,要求高性能低延时,需要尽量减少服务调用链路,这种情况比较适合把对外部系统调用逻辑和方法以组件化的方式来提供,如果业务本身对性能要求极高,在很多情况下会优先考虑性能问题而容忍组件化带来的一些劣势;
公司对于服务器资源成本控制要求较为严格,尽量降低成本,这种情况对于初创公司或者项目较为常见些,经常是要求是低成本快速试错。
案例一
应用商店月活用户2.4亿,日活6000万+,对商店服务器性能要求非常高,商店内部很多服务都涉及到了大量的外部系统服务调用,比如CPD、游戏、DMP等等,在用户体验和性能优先的前提下,我们都是通过组件化的方式来集成对外部系统的调用,容忍一些在维护和升级上的不便。
案例二
再分享一个商店使用组件化方式的一个反面案例,商店内很多服务模块都涉及到一些运营资源位的管理,很多服务都需要向客户端下发一些运营位的资源素材,在最初没有充分考虑各类场景问题,最明显的就是这些资源素材的获取都涉及到数据库资源的连接和调用,在使用组件化后会导致我们开发维护成本高,迭代效率低。
服务化后可做到资源隔离,互不影响,对调用方隐藏内部细节,可独立进行开发部署,提升开发效率;
相比组件化来说可维护性更好,服务化之后各个模块服务之间是解耦的,不管是调用其他服务的逻辑方法需要变动,还是第三方提供的jar包需要升级,只需要该服务本身升级即可,只要接口协议不发生变化,调用了该服务的其他服务都不需要变动,维护起来非常的方便;
服务升级成本低且风险可控,不管依赖了该服务的系统有多少,我们只需要处理好这一个服务的升级,开发维护升级成本、测试验证的成本及运维的成本相对组件化来说都极大的降低,风险也小,在现在各类jar包安全问题频发需要及时升级修复的情景下,服务化的优势显得更为明显。
服务性能相对组件化来说较差一些,服务化拆分的越多,服务之间的相互调用越复杂,调用链路也会变的更长,服务之间的网络请求调用越多性能越差;
服务化后多服务多节点部署,会带来一些天然的分布式系统固有的问题,比如一致性、分布式事务处理等,另外就是节点变多、服务链路变长带来的服务整体稳定性下降问题等;
服务化后会增加更多的服务器资源成本,在服务调用链路上每独立化部署一个服务,为了确保服务性能,对应的就会增加原有服务相应的机器资源数量或者更多,服务拆分越细成本越高;
那么哪些场景适合使用服务化的方式来部署呢?符合以下场景特点的建议使用服务化的方式:
针对自己系统内部的一些公共功能模块,如果涉及到了数据库层面的资源调用,建议使用服务化的方式提供,避免所有依赖该公共功能模块的服务都要配置维护对应的数据库资源信息,后面维护起来会非常痛苦,比如数据库变更、机房迁移等;
针对外部的系统服务调用,如果有很多内部系统都对外部服务有依赖,但是服务并发量较小对性能要求不高,服务调用链路变长影响也不大,对并发量和性能要求不高的业务通畅也不需要太多的服务器资源,这种情况下建议把对外部系统接口调用封装成独立的服务,当对外部系统调用逻辑发生变化或相关jar包升级场景下我们只需要升级这一个服务,不需要自己系统内部所有相关服务都升级。
案例一
系统内部公共功能模块服务化的案例,应用商店各个模块在返回信息给客户端之前经常会有一些公共的过滤逻辑,而这些公共过滤逻辑的处理还涉及到跟mysql及redis进行交互,因此把这些公共过滤逻辑直接独立拆分为一个独立的服务,目前该服务不仅对项目内提供服务,还会给公司内其他部门很多业务使用。
可能有同学会有疑问,针对用户量大,对服务并发量和性能要求较高的服务,多拆分一个服务出来解决了数据资源隔离的问题,但是如何解决服务调用链路变长导致性能下降的问题,比如上文提到的那个运营资源组件化的反面案例(示例图6),针对这种我们可以通过在调用方增加缓存的方式来解决性能问题,因为这个业务场景对数据的实时性要求并不高。
可能有同学会有疑问,针对用户量大,对服务并发量和性能要求较高的服务,多拆分一个服务出来解决了数据资源隔离的问题,但是如何解决服务调用链路变长导致性能下降的问题,比如上文提到的那个运营资源组件化的反面案例(示例图6),针对这种我们可以通过在调用方增加缓存的方式来解决性能问题,因为这个业务场景对数据的实时性要求并不高。
案例二
外部系统服务调用服务化案例,我们有一个开放平台系统,该系统主要是服务于开发者,对系统的性能要求不高,其中有一个需求涉及到外部系统服务调用,且开放平台系统内有多个工程都需要调用该外部系统服务,为了便于后续的服务维护和升级,就把对外部系统服务的调用逻辑统一封装到了一个专门用于外部系统调用的内部服务里,然后通过该内部服务来调用外部系统。
总结下组件化和服务化各自优劣:
综上所述,组件化跟服务化两种方式没有绝对的好坏,各有优劣,具体该使用哪一种方式跟我们的真实的问题场景有关系,大家可以参考以上的分析,结合自己的实际项目情况去选择符合自己的方式,技术最终是要服务于业务,大多数情况下没有最完美的解决方案,只有最适合的解决方案。