背景
2017年正直互联网金融崛起之时,由于那时候监管不规范,互联网金融公司如雨后春笋般崛起,我也是这时入职了一家互联网金融创业公司(一个大集团的全资子公司)。入职之前可能因为公司没有技术团队来开发一套完整的贷款系统,因此外包了系统给一个外包公司(这里就不说是哪家了),整个项目软件技术偏老,JDK当时还是用的1.7,外包系统可能大家都懂的,它们为了批量生产,基本都是有一个自有框架,然后在其框架上做开发。维护过外包系统的人可能都知道,那是一个非常痛苦的,就拿我接手的这个项目来说吧,我主要列几点。
- 数据库表冗余严重:数据库的表就有200多张表,仔细看了一下,大部分的表都是其为了方便开发各类金融项目整合的一个很庞大的系统,我们的系统可能只用到其中很少的一部分。表中很多冗余预留字段,要让一个新人短期内接手开发,几乎是不可能
- 前后端分层混乱:大量的jsp页面有sql语句,导致代码调试困难
- 严重的事务问题:系统中由于开发人员水平参差不齐和为了追求进度导致很多业务逻辑没有理清,比如将调用第三方系统的代码放进了本地事务中,导致本地事务异常回滚时,第三方无法回滚。当时是因为这个出了一个线上事故,因为生成还款计划问题,导致本地事务回滚,引发了一次用户疯狂提现却不减额度
- 维护困难:当时几乎每一个新人加入团队,每天下午培训一小时,起码要培训一个月才能上手开发,排查问题困难
出于这些原因,核心团队立志要将这个庞然大物重构,由于当时流行微服务,因此决定采用微服务的方式拆分。因为大家这个时候经验都不丰富,技术团队几乎没有任何积累。做起来,真的困难重重。我们重构主要分为3个阶段,第一阶段,稳定系统;第二阶段,自动化发布;第三阶段,微服务化
稳定系统阶段
由于创业公司,想拆分成两个团队来重构基本不现实。第一,外包项目交付时间不久,要想这个时候重构很难得到老板支持;第二,人手不足,第三线上问题频发。我们认为要想重构,必须要稳定线上服务
日志优化
由于该外包系统中,日志没有区分,全都打印出来,单台机器每天日志有3-4G文件,究其原因,原来是把客户上传图片文件base64编码打印出来,每一次查日志都是满屏的base64,所以第一件事便是整理这类日志,不再输出到日志文件
关键业务事务拆分整理
前边有提到过一个提现事务问题导致的一个生产事务,现在详细讲解一下这个问题,改造前事务如下图
可以看到,这里有一个严重的问题,就是将调用第三方系统揉在了本地事务中。可能出现两种情况
- 调用第三方系统timeout,这个时候我们能说调用失败了吗?我们不能确定对方是否收到了请求,因此这个事务不知道该不该回滚,如果对方没收到请求,那么回滚了没有问题,但是如果第三方收到了该请求,此时回滚就出现了事故,用户提现收到了钱,但是额度却没有扣,并且订单因为被回滚,导致订单都不存在
- 同样的,假设调用第三方没有任何问题,但是在生成贷款记录和还款计划时出现异常时,也会回滚,第三方支付平台已经支付,导致用户收到钱,但是额度没扣,订单没有
出现这个问题,事务没有理清或者只是为了完成开发任务完全没有考虑过这根本不应该做为一个本地事务,改造后的事务简单示意图。核心团队花了很多时间检查核心业务逻辑事务是否有问题,基本改造
自动化改造
之前的外包系统发布是增量替换class文件,说实话,这种他们认为的稳妥发布的方式真的很容易出问题。为什么没有一上来就改成自动化发布呢?原因有两个,第一是大家都不熟悉,不敢贸然改动;第二,当时公司的服务器用的是集团的机房,我们在外边无法访问服务器,平时看个日志都要通过teamviewer到厂区的电脑然后下载后传到本地看,不能霸占着厂区的电脑,因为大家都要用
服务上云
最先改造的是服务上云,和运维同事前前后后搞了一两周,各种配置文件,先用测试环境模拟服务发布,数据迁移,前前后后也模拟了两遍,怕出问题
代码管理上github
之前用的svn,用过svn和git的都会觉得svn真的不好用
文件存储上阿里OSS
之前的文件存储是自建文件服务器,并且前段他们用了一个不知道是什么的图片浏览插件,足足有100多M。我们的文件主要是图片和一些签约合同,OSS存储非常适合我们,那个插件被我干掉了,直接在页面上用标签展示图片
代码自动化发布
引入jenkins自动化发布代码,之前的替换class文件真的很容易出错,有一个用了jdk1.8编译,项目启动后出错了。改成自动化发布了,大家奋战了一晚,结果还是有很多问题,大部分问题是因为,之前驻场外包修复了某些bug后代码没有提交到svn,大家忙了一个星期基本把这些问题解决,从此服务终于不需要手动替换class文件
拆分微服务
服务拆分
服务拆分其实是很困难的,拆分的粒度,先拆哪部分都是经过很多次的讨论,最后的服务框架大致如下,只是个大概并非详细的设计。
- 基础服务层,主要是一些通用服务,和业务没有耦合关系,其数据库主要采用mongodb,因为主要是作为日志存储,并且mongodb灵活的结构非常适合我们业务的扩展,比如往往第三方数据格式都不一样。基础服务层除了给核心服务提供服务之外还给公司其他业务提供了服务,比如当时公司还有助贷业务
- 数据中心:主要是对接第三方数据公司提供风控相关的数据,该这个数据中心存储的数据分为实时数据和快照数据,实时数据主要提供给业务服务使用,快照数据通常提供给数据组做一些风控建模
- 支付网关:对接其他第三方支付公司
- 业务服务层,主要分为两块,一块核心服务,主要是贷款和用户额度管理模块;另一大块就是内部系统,内部系统都是针对公司内部人员使用。业务层采用的数据是MySQL,这一点也是业界标准解决方案吧。后边有一个助贷业务用了mongodb为业务数据库,mongodb在作为业务数据库还是有很多不足,当然也可能是我菜,设计得不好的原因
服务过度阶段
要想一上来就全部重构完,当然是最好的。可是,并不是每个公司都可以这样,其实做了这么久开发,也体会到了一些,明明知道什么是更好的设计,但是你就是没法那样搞。前文也说过了,老板花了几百万买了一个系统还没用多久,你跑过去说这个系统问题太多,要重构。作为开发,可能大家都会觉得重构的系统当然更好,但是如果缺乏管理层支持,真的很难做下去。
还有团结内部的问题,首先,需求还是源源不断的提过来。重构必须要有业务经验丰富开发人员推动,团队内部也会有很多矛盾,哪些人去重构,哪些留下来维护老系统,这个真的很难做到平衡,让团队每个人都满意。综合了公司环境和团队实际情况,我们并没有拆分出一个重构团队专门负责重构,采用了各人负责自己熟悉的模块拆分,优先完成产品部的需求。因此我们重构便是一个过程,所以,不得不有新、旧核心服务同时运行,逐步将旧核心服务完全替换掉
对于暂时无法拆分的模块处理
对于这种暂时无法拆分模块的处理,我们直接打成一个jar,在新核心服务中调用。当然可能有更好的方式
总结
一个团队从组建到逐步有自己团队的技术积累,到逐步重构掉一个折磨了大家很久的系统,期间也经历过很多困难,也有很多争论,比如在选型数据库时,大家都查了很多资料,犹如一个辩论的过程。让我体会到了人才的重要性,比如重构的开始,发现很多技术不足的情况,当时公司开的薪水并不高,别人技术好的根本不会来,总监也从最开始的我们要招大牛逐渐变成了我们要自强,真的见证了一个团队从一群小菜鸡慢慢变成各个都可以独立负责自己的模块。这并没有介绍细节,权且当成抛砖引玉。