谈谈运维发布平台的重构《一》

余梦

    工具组现在负责维护的运维平台应该算是技术中心内部很重要的一个系统,也是使用很频繁的的一个系统。自本人接手以来,一直在进行新功能的开发、遗留bug的修改。运维、技术、测试这边的需求不断,那会也抽不出精力对代码或者系统进行一些大的优化和重构。近期因为平台需求太多导致一个人根本顶不住,所以组内增加了点人手,分出去一些开发需求后总算是能腾出来做一些关于系统优化重构的事情了。

    说到重构,维基百科上是这样定义的: 指对软件代码做任何更动以增加可读性或者简化结构而不影响输出结果。 这个定义比较简单,主要体现在代码层面,很多人应该看过Martin Fowler的《重构-改善既有代码的设计》这本书,里面讲到了很多代码的“坏味道”以及很多代码重构的原则和实例。我这里要提到的重构,除了代码层面的,还有架构、业务层面的重构,这篇文章,我主要讲下系统框架及代码层面的重构。让我们看下百度百科对重构的定义吧:重构(Refactoring)就是通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。 我感觉这个重构的定义更接近我们平日重构的目的。


运维平台为什么要重构?

    回想一下,我们什么情况下会去做系统重构? 系统代码太庞大,功能堆积过多?部分模块代码很难维护、bug频繁、可读性差、逻辑不清晰、模块之间耦合度太高、性能低下、老的代码拓展性太差。。。。。等等很多原因,可能让你主动或者被动去重构现有的代码。运维平台算是公司很老的系统了,到目前为止,承担了公司大几百个服务的发布任务,随着公司业务的拓展和壮大,平台实现的功能也越来越多,承担的发布服务也越来越多,对稳定性可用性也要求越来越高。此前测试、运维、研发这边的需求也一直没停过,基本都在开发新功能,再加上维护过这个系统的人也很多,平台中也存在很多问题。说几个显而易见的吧,部分接口入参、出参结构定义过于复杂、系统模块多,功能都堆积到一个系统中、系统不支持横向拓展、前后端耦合、部分接口性能相当低、单点部署导致可用性大大降低、部分模块逻辑不清、代码杂糅,违背单一原则、部分接口没有做接口隔离细化,改动影响点较大。这些是比较明显的问题。本人接手后第一件事情就是findbugs全局check,scan一遍,干掉N多红色bug。上面都是运维平台里面的一些问题,这些问题,很多是不影响当前我们使用的,而且也不可能马上就解决完善掉所有问题。如同软件架构是为了应付软件系统复杂度而提出的一个解决方案一样,重构其实很多时候也是为了解决类似的问题,基本都是围绕高性能、高可用、高拓展等来展开的。这其中又涉及到时间、成本、安全、团队规模等因素的影响。考虑到目前运维平台的问题,我认为当前比较紧急的是系统可用性问题,以及可拓展性问题。高性能高并发这些至少在可预见的未来是不大会有这种场景的。

大局为重

  get到当前系统最迫切要重构的点外,接下来就是开始行动了。公司的应用服务基本微服务化了,也许有人会说,运维平台也服务化掉呗。那一套应用在现有运维发布平台上显然是不行的,不是说技术不行,成本问题,毕竟维护这个系统的开发人员有限,而且系统堆集了那么多功能模块,要想把这些模块拆分出来服务化,也不是一件很简单的事情,而且前后端分离后,如果后端全拆分dubbo服务,前端整个原有网关层全部需要重写,这个工作量不是一点点的。而且系统也没遇到什么性能瓶颈之类的。服务拆分除了增加可维护性、可拓展性,并不能带来更多有益效应。原来的系统后端用的springmvc,页面在同一个工程里面,每次前端发布,都是js打包扔过来替换掉原有的js,然后发布到线上,前端改个很小的东西我这边都要重新发布,很痛苦。痛苦倒也罢了,因为系统是单节点部署(为啥不能部署多个节点?因为不支持啊、不支持分布式Job,不支持分布式session,当然nginx按ip轮询勉强可以做到),所以每次发布都有有1,2分钟不可用,然后就有人会问我系统是不是挂了巴拉巴拉的。这个问题前面一直想改造却没落实,具体原有前面也说了。除了部署麻烦,另外一个头痛的问题就是调试不易,后来组内多投入了2个哥一起开发一些功能,每个人一个分支,开发环境只有一套,因为本地前后端调试很麻烦,前面大家基本都是扔服务器上调试。这两个问题是当前存在的主要痛点。公司现在服务基本都是基于springboot和dubbo的,我也就随大流,把穿来传统额老的web系统架构改成springboot的,这中间也遇到了一些坑,主要就是各种jar包冲突,系统依赖了很多其他模块和组件,有些版本不确定,有些因为不知道作用,也不敢随便移除、升级啥的。排除掉比较明显的冲突之后,就是关于springboot框架里面的一些jar冲突了。系统依赖老的parent.xml,里面和springboot依赖冲突挺多的,用过springboot的都知道,EnableAutoConfiguration,在jar包瞎依赖的时候很头疼,后来通过替换parent.xml,问题解决。再就是看以前系统配置依赖不少公司的一些模块,经过一些测试后,去掉了很多无关紧要的依赖,减少对其他模块的依赖。一个系统依赖的模块越多,稳定性越差,这次要改,索性改个彻底。去掉了很多没用的配置和依赖等。还有一个问题,老的系统用的过去传统的web项目结构,springmvc,web.xml配置还挺多的,特别是filter和listener也挺多的,系统依赖了cas,很多是和这个有关系的。这其中踩了一个坑,发现默认情况下,springmvc处理filter的时候是按xml里面定义的顺序处理的,想着既然已经改成springboot框架,索性能去掉的XML就全去掉,这里提醒下,springboot中设置filter执行顺序,网上很多文章怀疑自己有没有试过,靠谱的的方式还是实现Order接口吧。因为老的系统登录页面也是cas提供的直接回调平台的,现在既然前后端分离了,登录、鉴权这块,已经不再需要cas了,依赖的系统少了一个,稳定性感觉又提升了点呢。(前面确实遇到过cas服务挂了系统起不来等问题O(∩∩)O哈哈~)。我们在做系统重构或者架构改造的时候,一定要秉承几个原则,比如合适原则,简单原则、演化原则。并不是业界最先进、最高大上复杂有挑战性的技术、设计一步到位等就是最好的。前面提到,因为不再依赖cas了,鉴权、资源访问管理需要我这边重新实现了,原来系统没法横向拓展,现在需要支持分布式session,分布式job。job这块直接接入我们组维护的dajob,很简单的就实现了分布式job的功能。考虑到可能会有其他平台接入运维平台,调用相关接口、考虑到方便移动端等应用接入等,我这边使用jwt来实现分布式session的功能,也加强了接口调用的安全性。现在前端也分离出去单独部署,全部通过rest接口访问我这边的服务,tomcat压力也相对减轻了,前后端发布不再相互干扰、开发人员本地和前端、后端调试也方便很多,直接main方法启动就跑,也大大提高了开发联调效率,还有就是线上系统再也不会有人跑过来问我为啥挂了O(∩∩)O。


接下来改造优化的方向

    除了整个系统框架改造之外,还有一些性能非常低下的接口做了改造。现在系统遗留的一些问题就是有几个核心表设计不合理导致有些功能改造成本高、代码组织、页面交互上不合理,拓展性也不好,运维那边的回调接口功能过于庞大,需要拆分接口做隔离,减少各种回调操作的影响点,提高此模块的稳定性和可拓展性。再就是为了追求接口的通用而定义的一些过度复杂的数据结构(实际上并没有提高接口所谓的通用性,类的职责不清晰,导致功能杂糅,参数入参、出参接口过于复杂,大量无关的信息一并返回等问题,使得大家轻易不敢动这些结构)带来的一些问题。这块等完成后再写篇文章记录下吧,现在还在进行中。


一点总结

    系统重构的时候,一定要结合当前团队人员、时间成本、风险点等因素。如果是技术改造,一定要适合环境的技术,秉承合适优于先进>演化优于一步到位>简单优于复杂的原则。毕竟适合当前技术团队的才是最好的,系统的优化重构也很难一步到位,但是一定要记得预留进行演化的口子,但多时候,简单就是最好的。在组织代码上,很多时候,最简单的职责单一原则,就能搞定很多将来不要的麻烦,还有就是接口隔离原则,这两个原则,需要我们充分分离抽象出系统中各个功能、模块、接口,让我们清晰有序的进行代码的组织,而不会写出一个包罗万象、杂糅不清的类或者模块。一点体会,未必全对,如有不同见解,欢迎私下和我交流O(∩_∩)O