从单体架构到微服务架构的那些事

从单体架构到微服务架构的那些事

历史必然——单体架构

回顾自己经历过的后端系统,基本都是先从单体架构开始搭建的。初期的产品需求功能相对单一、简单,比如用户+核心业务模块两三个,就可以走完一个简单产品的功能闭环逻辑,另外产品第一版的上线时间一般又都有较为紧迫的要求,再加上初始团队配备的开发工程师包括运维工程师也比较少,一般一共也就两三人,所以在产品初期这个特定的时期、资源的约束下,后端系统采用单体架构是合适的,因为它足够简单,方便快速完成业务初期开发测试上线。

从单体架构到微服务架构的那些事_第1张图片

单体架构

系统第一版上线后,经过半年至一年发展,产品功能不断迭代,功能模块和代码量逐渐增长,参与团队开发的工程师也逐渐增加,单体架构面临的问题自然就逐渐显现出来了。最为明显的问题:

  1. 单个工程代码越来越臃肿。所有业务逻辑耦合在一起,代码越来越复杂,代码质量越来越难以保证,越来越难以维护,越来越影响研发的质量和效率;

  2. 线上单个服务进程承载的功能太多。服务部署越来越重,发布效率越来越低,而且很小的修改牵一发而动全身,降低了线上系统的稳定性。

更换引擎——微服务架构

面对单体架构的问题,解决办法就一个字,拆。微服务架构,可认为是目前服务拆分一种比较完善的标准方案。

按照微服务架构,总体拆分分为横向和纵向。横向拆分,就是把非业务部分拆出来,比如网关部分就是统一处理路由、鉴权、限流、熔断、降级、日志、黑白名单等。纵向拆分,就是按照业务功能模块进行拆分,依据高内聚低耦合原则(可以采用DDD思想),将各个核心业务独立出来,并单独部署。

从单体架构到微服务架构的那些事_第2张图片

微服务架构

总体拆分方案有了,具体落地执行更为关键和重要。

首先为了确保这件事顺利进行,在做服务拆分重构期间,团队一定不能再安排新需求开发工作了,这需要team leader具备一定向上管理能力和平级团队间沟通能力。另外,最好安排组内最熟悉代码的核心开发人员集中时间完成此事,然后进行全员code review,尽量确保工作高效,且拆分前后的代码业务逻辑的正确。

面对项目老工程几万几十万行核心代码,具体拆分并不轻松,并非简单的copy&paste,除了要对既有业务代码有足够的了解,这过程中还要涉及到大量代码重构的工作。历史的单体工程,在过往迭代开发中,因种种原因,太容易出现高度耦合的代码,重复的代码等bad smell,开发人员自己都越来越不愿意维护。因此,这项拆分重构的工作,一方面要按照微服务架构,把核心模块间的调用改为RPC方式,把非业务部分放到网关等,还要做大量业务类、接口、方法重新编写,以及删除重复代码等重构工作,很有挑战。

代码改完后,接下来的测试工作非常重要,除了研发自己完整的单元测试、集成测试外,测试工程师要进行完整的功能回归测试,以及性能测试,还要特别注意稳定性测试。

代码重构和测试完成后,就是最后关键的发布,将新代码平滑割接线上服务。此前线上系统已经平稳运行了很长一段时间,如何在不影响线上系统稳定性的同时,完成线上服务拆分的替换,这需要细致严谨的工作。

一般微服务架构会采用容器技术进行部署,所以需要提前搭建k8s环境。然后在其上部署重构后的代码,profile配置为生产环境。注意这里是在生产环境的平行空间部署一套线上系统,但前面网关先不放流量,测试团队通过内网转发完整测试新系统的前后端全链路。在待发布的新系统回归测试通过后,就可以进行线上流量的新老系统灰度切换了,这过程要紧盯线上系统的各种监控指标,确保业务正常,直到所有流量都切完,如遇问题及时切回老系统。事后再观察一段时间,老系统也同时保留一段时间。如果顺利,整个工作就完成了。

从单体架构到微服务架构,本身是一项技术优化工作,对技术团队来说是很有好处的,既偿还了一定的技术债务,组内成员又得到了一定的锻炼成长。而对业务来说,虽然没有直接带来新的业务增长,但替换了更加稳固和日后易于快速迭代的技术底座,为业务未来更好的发展提供了非常重要的保障基石。

方法论升级——DevOps自然到来

从单体架构到微服务架构的那些事_第3张图片

DevOps双循环

如果说单体架构时代因架构相对简单,还没有足够的动力让团队采用DevOps,那么当系统进化到微服务架构后,团队每个人内心逐渐会有一个声音在回响“我需要DevOps”。服务拆分,意味着服务数量指数级增加,这对人工是直接的打击,不论是开发还是运维,都需要更多的自动化技术辅助自己,以提高工作效率,以及工作质量,从而提升工作幸福感。

持续集成、持续交付是主要的自动化手段。代码提交后,经过一系列自动的静态代码审查、单元测试,自动集成部署,确保任何修改后都是ready状态,并随时交付给测试团队。减少了研发、测试、运维之间的不必要的沟通,提高了研发效能。

从单体架构到微服务架构的那些事_第4张图片

持续集成/持续交付

DevOps并不是微服务专属,也不是仅仅是CI/CD,它不是具体的某项技术或工具,而是一种管理模式、思想、文化理念,倡导团队通过工具和自动化减少不同职能(开发测试运维)间的沟通。在保证质量前提下提高研发效能,只要能达到这个目的都属于DevOps范畴。所以还有很多实践,根据团队具体情况不断完善。

除了整体研发模式的改变,微服务架构下,开发工程师在具体开发工作中还要做一些调整。比如以前开发,本地启一个服务就可以进行开发联调,现在需要同时启n个关联服务,想想就头大。为了提高工作效率,具体做法至少会有以下两种方式:

  1. 本地开发、本地调试、本地联调:

这种方式需要在开发本地完整搭建服务环境,可以采用Minikube一键搭建本地的单机k8s环境(建议安装k8s v1.22以下采用Docker容器),用Kuboard(或者Rancher、KubeSphere)作为k8s的可视化管理工具,服务依赖的MySQL、Redis等中间件都可以用Docker容器化创建。所有开发环境k8s内的服务、组件都可以整理为一系列可复用的yaml配置,任何开发都可以据此快速生成开发环境所依赖的所有服务、组件。不过,如果服务非常多,这种方式不太适用,毕竟本地开发机资源有限。

  1. 本地单元开发、本地单元测试、远程集成调试:

这种方式开发团队共用1-n个dev环境,这些环境中所有服务、组件都是完整正常运行着的。开发在本地写好某服务的代码并进行自测后,可以采用Nocalhost(或者Telepresence、Skaffold等)工具对dev环境中k8s的Pod实时替换,进行即时集成联调,或者是将dev环境对应Pod请求流量代理到本地服务代码,实现远程调试开发。这种方式适合于系统服务非常多的情况,不过要避免不同开发者同时联调同一服务造成冲突。

没有银弹——需要不断迭代

没有最好的架构,只有合适的架构。微服务也许也不是分布式架构的最终态,而Service Mesh、无服务Serverless架构也方兴未艾。随着时间推移,业务不断发展,技术架构必然要经历更新迭代。尤其对于互联网软件产品,不变的是变化,而且产品上线后面向用户,可能功能并不满足市场需求,甚至是无用的,所以一开始就在技术层面考虑过多,追寻完美不变的方案,是不现实的,而且很容易因过度设计,导致实现过于复杂,增加太多其实不必要的工作量,从而影响项目按时交付,以及后期应对需求变化的调整效率,进而挫伤整个团队士气。

记得自己刚参加工作时,所在团队的架构师就比较追求复杂完美技术方案,比如为了满足开闭原则,代码框架设计了相当多的分层,还有很多复杂的层次设计和调用逻辑,这直接导致程序员开发时增加了很多其实不必要的编码工作量,进而降低了开发效率,延长了开发周期,导致项目没有按时交付,引起一系列连锁负面问题。

遵从客观规律,架构是不断迭代完善出来的,不是一步到位,也不是一成不变的。所以技术团队的工作除了主要满足业务开发需求外,还有很多技术本身需要优化改造的工作。而架构改造这项工作从技术到执行并不是难到无法进行,但现实中往往难以真正落实,很大原因也许是来自于业务的压力。解决此问题,一方面需要技术leader有更高的追求,以及更强的沟通说服能力,另一方面更需要一个良好的团队文化和上层格局,认可类似的技术工作是有更高更远价值的,能积极鼓励技术团队用一段时间专心完成阶段性的技术优化工作,让业务在未来发展搭上新的飞轮。

你可能感兴趣的:(架构设计,Spring,微服务,架构,java)