lxue
作者lxue·2020-03-05 16:02
数据库管理员·某互联网公司

基于业务调度框架的证券业信息系统应用研究

字数 5939阅读 876评论 0赞 0

摘要

随着信息技术的快速发展,企业信息系统不断迭代更新,业务的办理形式也愈加多样化。外部环境竞争越来越激烈,促使业务部门产生快速迭代新业务、高频调整现有业务逻辑的诉求。但是,证券行业的业务往往逻辑异常复杂、业务系统耦合度非常高。信息技术管理部门在这样笨重且复杂的业务系统上进行持续迭代开发,势必会面临回归测试及业务监控困难等问题。而且上线迭代经常会引入新的生产问题。这些问题都说明,传统的应用系统开发模式已经满足不了精益模式的软件开发需求。
为了应对业务的快速变化,同时保障系统的高速稳定运行,本文提出一种基于业务调度框架的证券业信息系统设计方法。业务调度框架的核心原理是Pipeline管道模式,包含管道驱动机及状态机两个部分。业务调度框架可以统一编排调度各项业务流程,且具有完备的调度监控、业务执行监控能力。根据证券行业集中运营的特点,证券信息系统在设计的时候可以引入业务调度框架。依靠业务调度框架,可以将网上开户、一柜通业务办理等中的各业务生效步骤抽象成业务原子。由业务调度框架负责业务原子的编排生效,并保证业务原子执行一次且唯一一次。通过调度框架将复杂、重复的编排任务封装起来,统一调度、统一监控。开发人员可以集中精力在业务原子的开发上,减少无效、重复的劳动,企业也可以用比较小的成本适应快速的业务调整。

关键字:

证券业信息系统;业务调度;Pipeline;业务原子;状态机

注释:
业务调度 - 将系统的业务原子根据一定的规则进行编排并执行
Pipeline – 一种连接模式,将原本独立运行的节点连接起来
业务原子 - 将业务按业务数据/类型拆分,拆成相对独立可共用的原子能力
状态机 - 根据一定的规则,在有限的状态之间进行转移和动作

一、 引言

互联网的快速发展,带动着企业业务系统不断优化。其中证券行业的业务系统不仅面临业务逻辑及用户体验优化的驱动需求,还受到监管政策的强制要求。监管需求往往时间短、任务重且要求高。如何在保证系统质量的情况下满足监管需求是证券信息系统的重大挑战之一。传统开发模式中,研发人员通过硬编码的方式固定死业务原子的执行顺序。如果需要修改或者更换业务原子步骤,对源代码的改造量较大,且容易产生不易发现的漏洞。本文介绍的业务调度便是基于这一背景下产生。通过引入业务调度框架,软件研发人员可以专注于实现业务逻辑。业务变化不再会造成整个业务系统流程的混乱,更不会对现有业务系统造成严重的冲击,系统运行成本也随之缩减。
业务调度框架的核心是可以调度编排的业务原子集,因此业务调度框架必须是能够支持一定顺序配置,以支持业务原子灵活顺序执行,同时业务调度框架必须提供一个统一的业务原子沙盒,让研发人员可以在业务原子沙盒中实现业务逻辑,而不用去关心沙盒与沙盒之间的执行逻辑。
本文提出基于调度框架的证券信息系统设计方法,采用Pipeline管道模式提供业务原子沙盒的框架结构(Node),同时采用轻量级的流程引擎(也称状态机)实现业务原子集执行顺序可配置化。

二、 业务调度框架简介

介绍业务调度之前,先介绍下Pipeline管道模式。Pipeline本身是一种设计模式,这种模式的最早发明者是Unix系统的创建者之一,Pipeline模式在很多场景都有非常丰富的使用,从工业的生产流水线,比如富士康的工厂生产线,到我们日常非常熟悉的软件容器,比如大名鼎鼎Tomcat,便是从Engine到Host再到Context一直到Wrapper,都是通过同一个责任链,来传递请求;再到非常著名的通讯软件Netty,也是基于Pipeline框架,框架中增加了Handler处理机制,由Pipeline驱动Handler进行流水线式的程序执行。
本文的业务调度框架便是基于Pipeline设计的,Pipeline本身是种非常成熟的设计模式,在此基础之上结合了Netty的Handler的设计思路,自主研发出一套轻量级流程引擎驱动,如下为业务调度的核心设计图:

图-1 业务调度整体设计示意图

业务调度框架主要包括两大功能:管道节点驱动,业务流程管控。两大功能对应不同的业务场景,可酌情选型使用。管道节点驱动功能,适合业务处理节点复杂繁琐的场景,可将业务拆分抽象成独立的功能单元,并进行一定顺序的编排,后续业务原子单元可以被其他业务组合复用。业务流程管控功能,适合带有流程审核的业务场景,可以保证节点的结果幂等一致性。在海通证券有很多适合运用它的场景,客户的证券开户、一柜通业务等均可以使用此框架进行实施。

三、 与传统应用开发模式的对比

传统的开发模式下,在一些复杂系统中,假如存在某个业务对象需要被进行繁琐的逻辑处理时,一般是在一个组件下统一处理,这确实也达到了业务效果,但方式简单粗暴,随着业务迭代的持续发展,如要在繁重无比的业务逻辑中做一些修改时,看似简单的改动可能便会无从下手,望而止步,最终系统整体缺失良好的可扩展性和可重用性,代码臃肿,可维护性逐渐变低。遵守一定的开发规范,建设可扩展性好、可维护性好的应用架构,可以有效的规避传统模式下遇到的一些问题,归纳抽象、代码精简、风格统一、流程可编排、能力可扩展是一个应用架构良好的表现,这也是本次业务调度框架的主要价值。
下面通过几个维度的对比,以列表的形式展现了传统开发模式与业务调度框架各自的的特点。

四、 业务调度的应用架构定位

图-2简要的描述了下业务调度框架与业务系统的关系,以及在整体应用架构层面的定位。业务调度旨在结合具体业务场景在实际编码时提供更高效的开发方法,提升开发效率以及后续的系统维护性。业务调度框架以二方包的形式集成到业务系统,业务调度本身自嵌入轻量级的流程引擎(支持扩展实现自定义),为业务调度实现状态保持,业务调度的集成方式是非常轻量级的,业务系统的接入改造非常灵活,框架提供了便捷的工厂方法,可以在各场景灵活使用。目前支持两种使用方式:1)显式代码调用工厂方法直接创建并驱动2)与spring集成,通过bean的方式进行管理。

图-2业务调度在应用架构中的定位

五、 业务调度框架的设计

本节会重点阐述一下业务调度框架整体的设计与实现,业务调度框架从功能实现上来说,有两大核心的模块:管道驱动、管道状态机,下面将针对这两大模块的设计方案展开描述。

1) 管道驱动模块

首先介绍几个核心概念:Executor(执行器)、Node(节点)、Context(上下文)、Listener(监听器)。一次完整的管道驱动的运转逻辑如下:管道执行器Executor模块,将有序的Node集纳入到执行器中,执行器有统一的Context上下文,上下文中存放着业务对象数据以及环境信息数据,Context贯穿整个执行器的生命周期,由所有的Node节点共享,每一个Node作为执行器的基础执行单元,通过上下文获取业务参数后执行自己的原子业务逻辑,Node的执行状态也可在状态机中保存,每个Node还可以挂接自定义Listener监听器实现前置数据检查、状态检查、权限认证、异常处理等,待Node节点全部执行完毕由Executor执行器返回业务结果。
整个管道驱动的设计见图-3

图-3 管道驱动模块设计

• Executor(执行器)
Executor是业务调度框架的整体执行器,推动整个业务节点的执行及结果处理。不同的业务场景可以定制不同的执行器,例如网上开户流程作为一个完整业务场景,有一个针对开户业务的执行器;临柜开户全流程是另外一种完整业务场景,有另外的执行器。每种执行器均有一个管道状态机流程定义,业务调度框架通过执行器将Node(节点)的执行情况登记在状态机中。执行器负责整体业务节点的调度与执行,扮演的角色是整体业务进行的推动者。
• Context(上下文)
Context上下文是由Executor使用,由Node集进行读/存的内存数据集。Context采用Key/Value进行存储,根据需要可再细分为各种不同业务场景的子上下文。Context存储的内容一般为各Node的输入、输出,可用于Node之间消息通讯。可见Context上下文扮演的是一个数据通道的角色,是管道执行器中业务信息的载体。
• Node(节点)
Node(节点)是业务调度框架执行的原子单元,每个Node代表一个相对独立的功能单元,代表一次完整的业务原子操作。为了更形象的说明Node的具体含义,以证券行业开户流程为例,基本资料保存、资金账号开户、三方存管绑定、风险测评保存是整个开户流程中的一系列业务操作逻辑,将其独立成单独的业务原子节点:基本资料保存Node、资金账号开户Node、三方存管绑定Node、风险测评保存Node。通过上述方式的细分,业务开发可以将原本耦合在一起的整体业务逻辑代码,分拆到多个独立的节点处理器中,每一个节点处理器是一个独立的类,将代码独立分隔成原子单元,这样对于后续功能的修改与维护也会非常方便,因代码修改而引入的业务风险也会大大降低。
• Listener(监听器)
Listener是对原节点业务的进一步细分而衍生出的产物。Node是业务调度的基础单位,是一个相对独立的一块完整业务逻辑,业务逻辑在实现的过程中,会有很多的业务判断,比如判断业务执行权限、业务执行的前置条件是否满足、业务逻辑是否在遇到错误时需要中断执行等。根据以上需求,以Node为宿主,衍生出了Listener(监听器)。Listener根据职责不同,进一步可以划分为前置条件监听器、异常处理监听器、后置监听器。


图-4 Node内部结构设计

如下简单罗列了下Node节点大体结构的抽象类代码实现,仅供参考:
public abstract class AbstractHandler implements Handler{
private String handlerId;
private boolean _FLAG_=false;
public void handle(T ctx){
_FLAG_=false;
setHandlerId();
try{
onPreListen(ctx);
beforeExecute(ctx);
doHandle(ctx);
onPostListen(ctx);
}catch(Exception e){
onException(ctx,e);
if(_FLAG_){
throw e;
}
}
}
简要描述:
1、 业务定义Node节点时需要继承此抽象类,对应实现doHandle方法。
2、 Context在前置监听器、执行前准备、执行过程、后置监听器、异常监听器中共享。
3、 每个节点Handler都有一个唯一Id,该Id会受调度编排调度,用于控制节点的执行顺序。

2) 管道状态机模块

管道驱动模块只负责各业务节点Node的调度、编排,各节点在节点链中具体的执行状态情况,则由状态机负责存储,状态机通过状态控制为节点的编排提供执行的幂等保障,保证每个节点执行一次且只会执行一次。
状态机的基本流程如下:

图-5 状态机流程示意图

本文不深入介绍状态机的实现,如下作简要说明:
a) 上述图中共包含ABCD 4个节点,ABCD为按顺序依次执行的4个节点
b) 如果状态机最终状态为 1111时,代表节点链执行完毕
c) 4位数字分别代表ABCD的执行位图,0代表未执行,1代表已执行
d) 状态机可以通过对位图的判断,知道应该在哪个节点续做,也称为断点续做

六、 实际运用

业务调度主要适用于拥有相对固定执行顺序的业务子集的业务场景,并且这些业务子集在一定范围内有大量复用、组合的场景,这非常符合证券行业的业务规则,证券行业的业务模式相对比较固定,并且大量的业务会有类似、组合的特点,如下从实际运用角度介绍几个常见的场景。
• 网上开户流程

图-6 网上开户流程

业务场景描述:
1、 客户通过手机、PC浏览器进行证券账户开户的场景,客户一般需要依次做手机号码验证、营业部选择、身份证上传、资料确认、视频见证、协议签署、风险测评、密码设置等步骤。
2、 客户在中间的任何一步出现中断之后,比如客户退出app,则下次客户继续开户时,需要自动识别客户已完成的步骤,并引导客户继续完成未做完的步骤。
3、 通过多个业务步骤收集客户的信息,在后续审核环节时,则根据业务顺序将客户的各种资料依次落地生效,并最终开出证券账号。
解决办法:
(一) 开户过程
a) 将如上的手机号码验证、营业部选择、身份证上传、资料确认、视频见证、协议签署、风险测评、密码设置分别抽象成一个Node原子节点业务,并单独实现。
b) 将如上子业务集抽象成Node节点类,统一纳入业务调度框架中,并配置一个状态机,用于记录用户已完成的步骤。
(二) 审核环节
a) 审核生效环节需要依次将信息同步更新至后台系统,将这些环节分别抽象成子业务,封装成对应的Node节点并单独实现。
b) 将如上原子业务集纳入到业务调度框架中,并配置一个状态机来记录执行状态,用于识别哪些子业务已执行,哪些未执行。
c) 业务调度通过状态机记录的状态,确保业务执行结果的幂等性,保证各个原子业务节点按照指定的顺序执行一次且唯一一次。
• 一柜通业务办理
一柜通业务办理是指证券公司各营业部统一受理客户的开户、资料变更、交易权限开通等各种业务,一柜通业务的具体受理模式与网上开户类似。营业部业务人员在现场将客户的资料、影像、视频、密码等逐个验证完之后,由集中审核岗负责业务生效,业务生效按照业务的类型,将资料、影像、权限同步各后台系统。
在此背景下,一柜通业务通过将各业务生效步骤抽象成业务原子,由业务调度框架负责业务原子的编排生效,并保证业务原子执行一次且唯一一次,通过调度框架将复杂、重复的编排任务封装起来,让业务系统开发人员可以集中精力进行业务原子的业务开发。

七、 总结

业务调度框架作为基础组件,可以被运用于业务环节比较固定,业务环节执行状态需精确控制的场景。业务调度的引用在特定场景可以大量降低开发人员的重复开发工作量,并减少业务控制导致的异常,基于统一业务调度的系统在日志、调度链、上下文留痕等方面都可以有非常良好的体验。

八、 参考文献

[1] 李林锋 . Netty权威指南[M] . 电子工业出版社 . 2014-6-1

本文转自微信公众号:上交所技术服务
作者: 周靖 任荣 王东 王洪涛 / 海通证券 信息技术管理部

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

0

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关问题

X社区推广