为什么 ?
- 技术框架采用比较老的框架,项目不是基于Maven,依赖管理复杂。
- 基于JFinal 框架,接口&参数定义不清晰。
- 代码多人经手,结构混乱,可维护性差。开发该系统的人,基本上都离职光,导致目前团队内没有一个人对该系统的代码熟悉。
- 可测性不强(大部分功能,无法通过Mock 第三方接口来充分测试,尤其是异常流程测试)。
- 无资损熔断机制(目前对于能力有缺陷的通道没有基于对方业务不可用的异常金额熔断机制)。
- 日志打印不规范,定位问题麻烦,无法根据日志进行告警设置(所有的日志基本都是INFO),导致出现故障,都是业务方先发现。
重构目标
- 采用基于Maven 的项目,统一管理依赖。
- 采用成熟流行的Spring Boot,重新抽象,提供统一的门面接口,对业务方屏蔽细节,代码结构清晰,提高可维护性、可扩展性。
- 变化与不变分离,防止每次修改污染到其他功能,导致每次上线,都要回归所有的功能,增加测试的工作量。
- 适配可测性需求,可以通过Mock 第三方接口进行异常模拟测试。
- 规范日志打印,方便问题定位,并做好异常告警买点,能够及时发现异常。
- 增加资损熔断
- 能够基于业务进行接口级进行灰度。
风险与对策
- 重构、改动大、开发时间短(封闭开发12天,投入人力3.5人)
- 测试工作量大,测试投入少(投入人力1人,预计测试5天)
风险点 | 策略 |
---|---|
银行对接 | 1、不改变给银行提供的IP. 2、不改变给银行提供的url 3、开发环境Mock 银行接口,达到可测性、充分测试。 |
入侵性 | 1、对现有系统上下文,实现0入侵,兼容现有的业务接口。 2、可以回滚 |
平滑升级 | 1、新老银行网关可以共存。 2、通过新系统,可以路由到老的银行网关,解决根据银行通道进行灰度的问题。 |
方法论
- 抽象:共性和个性分离,变化与不变分离。
- 职责清晰:基于SOA机制,系统之间依赖于接口,系统之间互相屏蔽细节。
- 系统等级化原则:原则上底层是不能直接调用上层接口,从而避免循环关系、等到等级化的原则。
- 兜底:从资损的角度来看,系统设计必须存在兜底的策略(控制资损为第一原则)。
- 敏捷开发:基于银行通道、接口来进行开发(基于Story),持续交付(解决测试工作量峰值),基于Story提测,避免测试等待、突然忙碌。
工具
工具 | 描述 |
---|---|
Mock工具 | 1、采用MockKoon 2、模拟正常返回 3、模拟异常测试(模拟各种Http状态、超时) 4、模拟各种业务异常状态 |
开发工具 | 1、Maven 2、Eclipse 3、Git(Github) 4、Jenkins 5、Sonarqube(代码静态扫描) |
技术工具 | 1、SpringBoot 2、Mybatis 3、Redis 4、MySQL 5、Nginx |
设计
系统架构
关键设计点
关键点 | 方案 |
---|---|
抽象 | 1、子系统依赖SOA(RESTFul)接口,对其他子系统的细节透明。 2、通过统一的门面,对上游业务提供稳定的接口,通用参数和个性参数。 3、Bank-Proxy、Bank-Front 具有统一的抽象模型:Bank。Bank-Proxy 采用代理模式。Bank-Front 采用构建模式、复合模式。 |
路由 | 1、(业务编码、通道名称、接口)—>Bank-Front—>Bank。 2、(业务编码、通道名称、接口、资费)—>Bank-Front—>Bank。 |
反应堆系统 | 1、通过队列、异步维护系统原则(等级化、非循环原则)。 2、Timer 采用指数退避机制进行补偿(保证性通知) |
补偿任务 | 1、处理确定的终态,对非终态(不确定的状态)采用补偿(或者Pending) 2、兜底(阻损策略,控制资金损失为第一原则) |
动力视图
系统流程图:
动力视图说明
- 简要画出子系统之间的调用。
- Timer:定时任务采用指数退避算法进行重试,到达一定的数次,采用固定的时间去尝试。
- 反应堆系统:采用队列、异步调用打破系统循环关系。
- Timer 与BankProxy的调用:BankProxy根据路由关系,路由到相应的BankFront。
事务补偿策略
补偿流程图
关键设计
- 状态未知:网络读超时,被调用方返回未知的状态码(该错误码不能说明成功或者失败,对于成功的错误码必须是明确的状态,同时失败的状态码必须)。
- 假如成功状态是200,那么失败的状态 禁止:非200的状态,失败的状态,必须列出,比如201,202。其他状态认为非终态,比如203。
- 补偿机制:注意幂等性操作,先查询、后补偿(一般是查询对方状态,更新本地状态)。
- 通道能力:被依赖的通道具备查询接口,则进行指数退避补偿。被依赖的通道不具备查询接口,则进行资损熔断。
兜底
做最坏的打算,系统设计总会有不尽人意的地方,所以必须做最坏的打算。 系统设计如此,人生亦如此。
正确认识成功或者失败
- 区分业务上的成功 和业务上的失败。
- 内部失败:比如写数据库失败。
- 如果是业务成功 + 内部失败,则可以返回调用方业务成功或者Pengding。
- 如果业务失败,则返回调用方业务失败。
一个血的案例
- 这个例子 由于没有正确识别业务成功 和内部失败,导致资金损失。
血案产生的流程:
资损控制策略
问题 | 策略 | 动作 |
---|---|---|
银行通道能力有缺陷 | 1、日常异常金额>= X 告警 2、日累积异常金额>=Y,通道暂时不可用,并告警 |
1、接收告警,要第一时间联系银行。 2、提供手工清除不可用标识 |
银行能力完备 | 走完整的事务补偿流程 | |
自动退款 | 1、自动退款日累积金额,超过日常金额,转手工确认。 2、单笔最大超过xx,转手工确认退款 |
防止自动退款由于某个异常,造成损失。 |
手工退款 | 1、提供异常订单查询系统 2、提供手工退款功能 |
平滑升级(灰度)
通过BankProxy 灰度
- 通过Proxy 基于银行通道的灰度
通过Nginx 灰度
架构优点
变化与不变的分离
- 反应堆系统(Reactor):依赖于抽象,逻辑是稳定的。
- Bank_Proxy:依赖于抽象,是稳定的。
- Bank_Front:是变化的,可以微服务(模块化),对变化的边界是可控的。
点击查看更多内容
1人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦