0
点赞
收藏
分享

微信扫一扫

新结算系统项目总结


新结算系统项目总结

  • ​​1. 项目介绍​​
  • ​​2. 项目划分​​
  • ​​2.1 设计阶段:​​
  • ​​2.1.1 旧结算系统的设计总结​​
  • ​​2.1.2 新结算系统架构选型​​
  • ​​2.1.3 新结算系统模块设计--模块设计​​
  • ​​2.1.4 新结算系统实体设计--er设计​​
  • ​​2.1.5 新结算系统关键算法--详细设计​​
  • ​​2.1.6 稽核设计​​
  • ​​2.1.7 交互设计​​
  • ​​2.2 开发阶段​​
  • ​​2.3 验证阶段​​
  • ​​2.4 发布阶段​​
  • ​​2.5 优化-维护阶段​​
  • ​​3. 项目中做的优点​​
  • ​​4. 项目中做的缺点​​
  • ​​5. 总结​​


本文只是介绍作者在公司中参与的一个项目,以及参与项目的感受。

1. 项目介绍

项目背景:因旧结算是c++单体程序,面临单体程序的性能瓶颈,而且旧结算程序由完全独立的6个字程序构成,子程序之间的调度性能低,c++程序调试,维护效率低,同时面临着没有专业的c++开发人员维护的问题。因此,计划对旧结算程序进行升级改造。

项目计划:在不改变旧结算程序的基础上,重构结算系统,简化程序复杂度,提升性能。由java生态中的大数据完成,因为java开发人员多,没有c++开发人员。增加结算系统中稽核的新功能。

项目参与人员:产品1名,开发2名,测试1名。

项目时间:没有准确时间,越快越好。(因为目前一个人有多个项目参与,随时面临打扰风险)

我在项目中的工作:旧结算系统就是我维护的(我主要还是java开发),因此对结算业务熟悉,同时熟悉旧结算系统架构,熟悉旧结算系统的代码和一些关键算法。同时因为参与2018~2020年结算,了解客户结算系统的需求和痛点。所以,我在整个项目中是核心设计者,核心开发者,同时提供旧结算系统的支持。

2. 项目划分

项目共被分为了5个阶段,2个部分。

5个阶段是设计阶段,开发阶段,验证阶段,发布阶段,优化阶段。

2个部分是结算部分和稽核部分。

2.1 设计阶段:

在设计阶段主要完成旧结算系统的设计总结,新结算系统架构设计,新结算系统的模块设计,新结算系统的实体设计,新结算系统的流程设计,新结算系统的关键算法设计。

2.1.1 旧结算系统的设计总结

在最一开始,参与项目的人员需要了解旧结算系统,需要了解结算的业务需求。而我主要是总结旧结算系统的业务需求,旧结算系统的实体结构,旧结算系统的业务流程,旧结算系统数据流等。

这个阶段,我需要负责给参与的其他人讲解旧结算系统的组成,旧结算系统的使用,以及如何从0开始搭建一个旧结算系统。

在这个过程中,总结输出了旧结算系统的er文档,旧结算系统的业务设计文档,旧结算系统的安装配置手册,旧结算系统的使用说明手册。

当然,旧结算系统也不是完全没有文档,可以借鉴大约25%左右。

2.1.2 新结算系统架构选型

因为是基于java生态圈,因此待选的架构当时只考虑从spring batch和sprak中选择一个。

在大家讨论确定架构时,当时主要是2个开发人员和一个领导以及其他两个开发人员参与(一开始确定参与开发的人员是4个)。

经过讨论,最终确定使用spring batch。

原因如下:

  • spring batch是spring 家族的一员,与公司新系统的微服务模式兼容。
  • spring batch是纯java开发,公司目前全部的开发人员要么是java后端,要么是前端。
  • spark需要额外了解spark大数据的其他架构,技术门槛较高。

其实我个人对使用什么架构没有一定的倾向,这两个对我来说都是技术盲区,都需要从零开始学习,然后才能参与开发。

而且这个项目对我来说是一个非常好的增加项目经验的机会,所以不管是选择哪一个技术栈,我都需要从0开始的。

但是为了跟上大家讨论的思维,还是简单的了解了下这两个架构的特点。

总的来说,公司的数据量确实还没有达到必须使用spark这样为超大数据量量身定做的地步,不管是spring batch还是spark都能满足新结算的性能需求。

其次,spark想要运行起来,需要了解一些配套的比如HBase,HSQL,等等一些额外的技术栈。光熟悉这些技术栈可能就需要很长的时间。

而spring batch则不一样,它和其他spring 架构一样,只需要了解很少的知识,你就能上手开发。

所以,最终大家一致决定使用spring batch。

为了更好的使用spring batch开发,我系统的学习了spring batch.

​​spring batch技术专栏​​

github地址:

​​https://github.com/a18792721831/studybatch.git​​

文章列表:

​​spring batch 入门​​

​​spring batch连接数据库​​

​​spring batch元数据​​

​​spring batch Job详解​​

​​spring batch step详解​​

​​spring batch ItemReader详解​​

​​spring batch itemProcess详解​​

​​spring batch itemWriter详解​​

​​spring batch 作业流​​

​​spring batch 健壮性​​

​​spring batch 扩展性​​

2.1.3 新结算系统模块设计–模块设计

在旧结算系统中,将结算分为了6个子程序,这些子程序之间完全独立。这6个子程序分别是采集、预处理、批价、出账、账前回退和账后回退(在电信行业结算基本上都是这几步,算不上公司商业机密)。

当初在开发的旧结算系统时,大概是在10年左右,当时机器的性能比较差,因此选择了临时文件作为本地缓存数据的方式,以此来提升性能,同时为了保证程序的有效性,因此将整个结算系统分成了多步。

旧结算系统的子系统执行有着严格的顺序,必须先采集=>预处理=>批价=>出账。如果中间的哪一步出错了,还必须先使用回退程序,然后重新执行。

整个旧结算系统操作起来非常繁琐,而且非常容易出错,采集和预处理都是在服务器的内存中操作,操作的都是临时文件,因此如果机器宕机,那么数据是找不回来的,也没法记录断点更没有办法快速恢复。

结算是每月一结算,旧结算系统因过程容易出现问题,因此需要手动执行。成本非常高(人是最贵的)。

基于这些痛点,加上现在机器性能提升,以及新架构的支持,新结算的系统模块进行了非常大胆的改进。

将原来6个子程序或者说模块,直接合并为1个。

合并为1个模块,到底是进步还是退化呢?

在旧结算系统中,分成多个子程序,最核心的原因是前置操作的有效性。比如结算最后一步出错,那我前面的已经完成的操作还是有用的,并不是完全无用,需要丢弃的。因此,在旧系统中,分成多个子程序,就是保证前置操作有效,不会因小失大。

在新结算系统中,使用的是spring batch架构,spring batch架构本身就支持快速恢复,断点记录等功能。即使在某个时间点程序异常或者宕机,也可以快速恢复的。

在旧结算系统中,使用临时文件,是因为在旧系统中,需要将数据全部加载到程序中,才能进行操作。

而在新系统中,是分批处理的,一次只处理一批就行。

所以,在旧结算系统中,内存的要求是很高的,都是在20G以上。在新结算系统中,内存的要求非常低,甚至最小配置为256M的机器都能运行。而且支持根据内存调整(控制分批中每一批的数据数量,也就是spring batch的chunk的数量)

旧结算系统中,子程序之间的调度需要有一个程序不停的死循环询问操作是否完成,是否进行下一步操作。性能比较慢,而且还耗费资源。

新结算系统中,只有一个模块就不存在调度的问题了。

旧结算系统中的每一个子程序都是有状态的,执行的顺序也是必须的。

在新结算系统中,将6个子程序融合为一个整体,将结算程序变为无状态的。

综上,我认为将6个模块合并为1个,是在新架构的支持下,考虑现在硬件资源而做的一个大胆的改进。

2.1.4 新结算系统实体设计–er设计

新结算系统的实体设计中摒弃了旧结算系统中一些无用的,或者可以去掉的一些配置,同时结合新架构,增加了一些配合新架构使用的数据表。

毕竟旧结算系统已经使用了10年左右了,整个结算的业务比较成熟,而且还不能很大的修改结算的界面使用习惯,因此这部分改动很少。

2.1.5 新结算系统关键算法–详细设计

在旧结算系统的c++程序中,有大量的for循环和if判断,以及很多的自定义异常,通过异常做正确的业务流程控制。

在新结算系统中,为了改进这种编码风格,同时新系统是基于jdk11的,因此大量的使用了lambda表达式,以及jdk8中的流的操作。

同时改变了c++用异常控制流程的风格,而是使用数据驱动流程。在进行某个操作之前,使用流的过滤操作,保证操作不会出现预期之内的异常。至于预期之外的异常,本来就该继续抛出,而不是直接忽略。

同时为了去除或者说避免多层嵌套代码,使用了jdk8中的Consume,Function和Predicate函数。以及这些函数的组合。

这样消除了多层嵌套代码,同时,代码的密度也是c++代码密度的10几倍。

2.1.6 稽核设计

稽核部分是在结算部分之后的,有了结算部分的经验,稽核部分也大量参考了结算部分的很多经验,所以基本上差别不大。

稽核程序被分为了结算前稽核和结算后稽核。

稽核详细设计和模块以及er设计差不多的。

稽核部分完全由我一个完成,其他人主要参与审核。

2.1.7 交互设计

前面都是程序设计,还缺少了spring batch的交互。

spring batch的交互考虑了2种方案:spring batch admin和xxl-job。

考虑到交互的习惯,选择了国人开发的xxl-job作为spring batch的交互。

全部的设计都有相关的文档输出,包括模块通信图,er图,状态装换图,数据流图和流程图。

2.2 开发阶段

当设计阶段完成后,并且设计通过了审核后,就到了开发编码阶段。

现在公司微服务的编码都是符合DDD设计思想的。因此代码的开发在DDD的指导下,完成开发。

flyway管理脚本;统一的日志;jpa做为核心的dao。统一的异常等等。这些都是统一的编码规范,需要通过sonarLint检查才能提交。

在开发中,也使用了guava的本地缓存加快性能。

大量的注释等等。

新结算系统项目总结_项目经验

编码中使用函数,实现了一些有意思的写法:

比如基于函数实现责任链模式,

基于函数实现简单工厂模式,基于泛型和函数实现构建者模式,状态模式等等。

新结算系统项目总结_项目经验_02

2.3 验证阶段

验证阶段主要是测试人员验证,出现问题之后快速修改即可。

整个项目共有13个小问题。编码的问题率还是很低的。(代码基数不小)

2.4 发布阶段

整个项目采用docker镜像的方式发布,采用docker-compose管理。

所以需要将应用程序先打包为镜像,然后开发docker-compose的配置,最终给出新结算系统的安装配置手册和使用手册。(这两个手册都是我开发的)

将应用程序打包为镜像,使用的是jenkins打包,打完包后将镜像上传到harbor镜像私有仓库中。

然后开发docker-compose的文件,在现场机器上使用docker-compose启动即可。

2.5 优化-维护阶段

发布之后就是优化阶段了,这个阶段也是最长的了。在我写这篇文章的时候,项目正处于刚完成发布的优化-维护阶段的初期。

3. 项目中做的优点

  1. 系统的学习需要的架构,为开发与设计提供技术基础。
    为了能更好的在spring batch中开发与设计,进行系统的学习。(系统的输入才有系统的输出)系统的学习了之后,才能根据spring batch的特点,进行合适的设计与开发。
  2. 积极使用jdk新特性,改变不好的编码风格。
    jdk8的新特性,可以在很大程度上提升代码密度,减少无用代码逻辑,直击数据。
  3. 编码中遵循DDD设计思想。
    在DDD思想的指导下,开发的代码都是框架无关的,从spring batch框架,切换到其他框架,只需要改动很少的几个对外接口,内部的核心逻辑是不需要做任何改动的。
  4. 融入设计模式
    没有设计模式,我们也能实现这些功能。但是使用了设计模式,可以更加科学的实现功能,对之后的可维护性和程序的扩展性,健壮性都有很大的提升。
  5. 使用本地缓存
    频繁的交互数据库肯定不行,最好的解决方式是使用专门的缓存redis,但是又不能将新系统做的太过复杂,因此退而求其次,使用guava的本地缓存是一个不错的选择。既能增加减少数据库交互,有不会增加系统复杂度。
  6. 引入了nacos配置中心
    旧系统中的配置是文件配置,没有版本管理,没有格式检查,没有动态刷新,没有权限管理。谁都可以修改配置,修改完还不知道对不对,只能重启试试。
    新系统使用了nacos配置中心,有版本管理,有格式检查,能动态刷新,有权限管理。正好解决了这些痛点。
  7. 性能的提升
    说实话,在做之前,我是不确定是否能完全满足需求。但是至少从已通过的验证来说,全部的痛点和新需求都满足了。特别是性能上,是老系统的10倍以上。

4. 项目中做的缺点

  1. 伪集群
    新结算系统不是严格意义上的集群,但是新结算系统又能在xxl-job的支持下,多机器配合。所以,我认为这是一个伪集群。xxl-job是一个分布式任务调度系统,xxl-job是支持分布式的,因此虽然新结算是单体的,但是却能启动多个实例,用xxl-job进行调度,近似实现集群。
    不过,这样也有缺点,当两个实例操作相同的数据时,存在并发问题。这也是伪集群的一个致命问题。
  2. 没有进行性能验证
    公司数据量太少,所以就没有进行大数据量下的性能验证。但是就同等数量下的性能验证(现有数据量,万级,生产是百万级),性能提升了10倍以上。
    不过这个性能还需要在大数据量下进行验证。
  3. 不能滚动部署
    最好的部署是基于kubernates下的滚动部署。
    但是客户没有kubernates的维护售后人员,因此选择一个折中方案,使用docker-compose进行部署。
    这样就不能实现自动化滚动部署,出现问题不会自动回滚了。
    只能手动部署,出现问题手动回滚。
    不过,这个倒不是很严重的问题,因为这个结算系统只是给少数的和财务有关的维护人员使用,难用点,客户到是能接受。

5. 总结

整个项目从2020年11月开始到2021年2月中完成,历时3个月半。期间对旧系统进行全面的总结,学习新架构,进行新系统的设计。包含了架构设计,模块设计,实体设计,流程设计,关键算法设计。设计审核评审通过后,结合科学的开发思想指导,完成编码任务。其中用到了DDD开发思想,jdk高效特性,比如流操作,lambda表达式,函数等,以及设计思想的知道,统一规范的异常和日志,以及提交前sonarLint的检查等。在完成验证后,开发部署脚本,编写手册。开发docker-compose的部署脚本,编写安装配置手册,编写使用手册。

整个项目难度不大,因为我本来就是维护旧系统的人员,因此在业务方面基本上没有难度。挑战是如何快速的学习新架构,并快速的学以致用,在新的设计中进行体现。如何以不同的层次,进行系统的设计,设计完成后,如何评审自己的设计是否合理,能否站在不同的高度,不同的角度评估设计。接下来就是如何将设计进行诚实的实现。(在开发结算的时候,出现了编码与设计不一致的问题(就是本地缓存,设计时没想到,实际编码的时候,增加的),在开发稽核的时候,在一开始就设计好了)最后则是整个项目的部署上线,如何站在使用者的角度,以最简单的方式教会使用者使用新系统,如果使用者不能快速上手使用,那么是不是新系统的交互太过复杂。以及站在使用者的角度上,考虑新系统的部署,新系统的使用,新系统的维护,以及新系统的参数调优。

在进行这个项目之前,我只是负责编码实现,从来不会考虑为什么要这样实现,更多考虑的是如何实现,是使用for循环,还是使用while循环。经过这个项目我学到了很多。

这是迄今为止,我最完成的参与的项目了。

在项目中,体会最深的就是设计。整个三个半月中,抛去过年的半个月,那么实际上就是三个月,在减去打扰时间,真正的时间应该在2个月多一点。

但是实际上,结算和稽核的设计开发,设计评审就花费了差不多1个半月左右。实际编码只占全部的大概四分之一左右的时间。但是这个确实让我体会到了设计的重要性。(就像网上流传的一句话:选择有时候比努力更重要)当设计不合理的时候,开发出来的程序只会更加不合理。

在整个项目中,随着时间的进行,我自己的角色实际上也在发生变化。

一开始是旧系统的使用者,此时我要找到旧系统的痛点和不足。同时也要发现旧系统好的地方,在新系统中予以保留。

接着是设计者,我要在全面了解需求和现状的基础上,构思和设计新的解决方案,并准确简明的表达出来,这样别人才能认同你的观点,给与你支持。

设计者是细心的,设计者是全面的。设计不可能一蹴而就,不会一下子达到目的,需要不断的优化,反复考虑程序的细节,才能写出好的设计。

接着是开发者,当设计已定的时候,拿到设计,需要准确的使用编程语言实现设计。在开发阶段,我觉得更像是一个翻译,将设计图纸翻译为代码。那么如何保证翻译中的信达雅,就是在开发过程中需要考虑的了。

验证测试我只是修改出现的问题,实际测试我是没有参与的。(当时一边修改结算的bug,一边设计稽核)

最终是使用者。在发布阶段,需要忘记自己已经掌握的技能,假设自己是一个什么都不同的人,那么,如何教会一个什么都不懂的人使用新结算,部署新结算,就是在这个阶段中最核心的任务。

在项目中随着角色的变换,考虑问题的侧重点也在不断的变化。不过,总体而言,这个项目对我的意义很重要。

让我明白了一个项目从立项,到设计,到开发,到部署上线的全过程。亲身体验了这其中每一个过程需要进行的操作。


举报

相关推荐

0 条评论