基于微服务的工作流技术在云管平台的应用
2019-09-28罗钦凯倪成章
罗钦凯,倪成章
(1.华中科技大学,湖北 武汉 430074;2.武汉邮电科学研究院,湖北 武汉 430074)
0 引 言
全行业大规模的“上云”需求,和以OpenStack为代表的云计算资源类型的多样化,极大扩展了云管平台(cloud management platform,CMP)的服务领域,亟待实现一种架构层面直观反映复杂业务逻辑的、便于敏捷开发与持续集成的CMP开发方案。单体架构已不能满足软件即服务(software-as-a-service,SaaS)层应用在高扩展性和高可用性等方面的需要,亟待一种新的设计架构能最大限度地实现高扩展性。
单体架构应用开发成型迅速,但随着线上业务需求复杂多样化,新需求调研阶段的沟通成本显著,业务逻辑开发困难,无法满足应用对扩展性和维护性的要求[1],也将导致需求调研阶段需求沟通的成本显著和上线维护阶段可维护性差。从开发实现业务逻辑的痛点出发,通过解构工作流核心设计模型,设计出工作流微服务组件结构模型,并基于Spring Cloud微服务开发框架给出组件间REST API与流程节点自由跳转算法,以及CQRS模式数据操作等方案。
1 研究背景
工作流技术顺应了信息时代“异构化、松耦合”的发展趋势[2]。工作流相关领域的专家学者对此做了许多研究。例如,于乐等[3]设计了云工作流在商业智能业务场景下的SaaS层应用;邓丽等[4]设计的空间科学任务协同论证平台论证了复杂任务下的协同性与一致性;李金艳等[5]实现了一种面向协作的基于角色的柔性工作流访问控制机制;陈儒等[6]研究了基于事务规则驱动的动态工作流模型;张型龙等[7]设计并实现了基于服务集成的工作流模型。针对工作流技术应用于传统单体架构时功能与业务逻辑耦合度较高所带来的扩展性差、可维护性差等问题,文中提出采用基于微服务的工作流技术,将面向分布式服务的工作流[8]应用于CMP,以期实现对分布式云计算资源的调度管理。
1.1 工作流
工作流技术沿着以数据为中心的流程建模与柔性分布式设计的方向发展[9]。业界提出旨在提供易于理解和易于标识的业务流程建模标记(business process modeling notation,BPMN)。衍生的BPMN2.0规范可以直观和准确地描述业务流程的细节,降低在整个产品生命周期中按需开发的沟通成本;有利于业务逻辑与功能逻辑进一步解耦,降低工作流技术在复杂业务场景下持续集成新功能的实施成本[10];它更注重语法定义的通用性和交换格式的标准化,XML格式规范便于移植在所有遵从BPMN规范的工作流引擎中解析利用。
1.2 微服务
微服务架构本质上体现的是解耦再封装[11]。由多个团队自由选择合适技术独立开发每个服务,只要遵守统一的系统内HTTP型无状态通信规范API即可,降低复杂系统的设计门槛,适合团队敏捷开发;每个服务均能被独立部署和验证,让持续部署成为可能,可以显著提高部署效率;只需要在特定的微服务组件中增加所需功能,而不影响整体业务和其他功能,整个微服务系统的可扩展性得到提升。微服务架构有别于面向服务的架构(service-oriented architecture,SOA),微服务架构各个服务间基于端到端的订阅方式,业务流程发生完全按照业务逻辑,当前事件的触发只需关注前置事件即可,需求变化带来的改变也只面向业务上下文而非功能,架构设计相当灵活。
2 工作流核心设计模型
主流的工作流引擎是jBPM5与Activiti。通用性方面,Activiti所采用宽松的Apache License 2.0开源协议不会在二次开发商用过程中引起不必要的著作权纠纷。易用性方面,微服务架构分布式的调用方式使Spring Cloud开发框架成为主流,Activiti架构继承自jBPM4,在整合已有jBPM4项目上具有原生优势,这也意味着它对Spring框架的原生支持[12],可以轻松集成基于Spring代理的事务管理以及基于业务逻辑的面向切面编程。
2.1 流程推进模型
使工作流流程推进模型首先要以满足以下定义作为前提:
定义1(状态转换):从一个给定状态出发,只能进入有限状态机中其他状态的一个子集;服务随流程实例的演进,完成调用其他服务或是某些需要手动确认触发的业务流程,流程状态从当前节点转移到下一节点等待被触发。
定义2(事务控制):下一事件只能在BPMN流程定义模型描述的前置事件完成之后完成,不能跳过任何事务步骤,否则不满足事务性。
定义3(定量描述):BPMN流程定义模型对流程的定性描述还需通过对各类流程数据持久化处理使其定量化。
BPMN流程定义模型结合Petri-Net[13-14]和UML两种建模思路实现Activiti工作流的流程推进模型。由起点(StartEvent)、终点(EndEvent)、任务节点(Activity)与连线(Transition)组成的有向无环图(directed acyclic graph,DAG)即为流程定义(ProcessDefination),实例化的流程定义即流程实例(ProcessInstance),实例中流程执行体(ProcessExecution)相当于指针。每个实例被加载进工作流引擎都会首先触发activityBehaviour()事件找到起点,紧接着会触发监听器(executionListener())执行起点的end()事件并找到出方向连线事件outgoingTransition();同样,触发监听器执行出方向连线的take()事件找到第一个任务节点Activity;触发监听器执行当前节点的start()事件,为当前节点设置属性、指派任务或调用API后,触发监听器执行当前节点的end()事件完成当前任务节点,并提交事务,将当前的状态保存到数据库里。该流程实例会暂停推进,直到外部请求再次触发监听器执行出方向连线的take()事件找到下一任务节点,流程会继续向下一步骤推进。图1是流程推进模型分镜操作时序图,每次流程推进都由一次或多次上述步骤组成。
图1 流程推进模型分镜操作时序
2.2 面向API调用
面向API调用是Activiti Service的设计思路。工作流引擎通过对外暴露Service API(见表1),依据流程定义部署流程实例、配置流程参数、指派任务执行人或代理人,实现审批、回退、驳回、创建等业务功能,完成工作流的内在功能逻辑。
工作流引擎未直接暴露的底层服务API也可以通过ProcessEngine API调用并借此完成节点自由跳转等特性功能。
由此衍生出的概念层面三种不同粒度的API封装模式如图2所示。
表1 工作流引擎Service API
图2 不同粒度的API封装模式
外观模式(Facade Pattern)下Service API是对功能逻辑的抽象定义,通过调用对外已经暴露的ServiceAPI或重写的自定义ServiceAPI实现功能逻辑;命令模式(Command Pattern)实质上是将不同命令(Command)交由命令执行器(CommandExecutor)统一执行;拦截器模式(Interceptor Pattern)下,命令模式执行当前某个命令的过程,对应于在当前命令执行前后分别执行若干前置或后置拦截器的过程,旨在实现最细粒度的事务控制。ProcessEngine API在外观模式层面将工作流引擎内部作透明处理,屏蔽底层架构实现层面上复杂的技术细节,使得调用服务不必关心引擎事务规则和流程定义等的具体实现。
3 工作流微服务组件设计方案
微服务架构(见图3)的设计理念在于,有编排功能的基于业务逻辑的架构可以减少耦合,在此基础上所有的微服务组件可以订阅与其业务相关的其他服务所发生的事件,进而对事件做出响应,最终通过REST服务完成功能。采用API网关机制聚合系统外部请求和映射系统内部服务,内部每个微服务组件提供REST API。请求基于HTTP协议通过API网关分发到目标服务,可以降低外部请求与具体服务的耦合度,减少通信频次。采用去中心化的服务注册机制,每个微服务组件的服务注册节点之间没有主次之分,针对注册节点宕机问题提供自动检测及周期恢复功能,所有组件通过引用服务发现客户端,在每个组件入口类上添加服务发现注解后即可完成服务注册。
图3 CMP微服务架构示意
3.1 微服务框架与工作流结构模型
面向领域模型(domain-oriented model,DOM)可以准确反映业务语言,真正实现以业务逻辑实体为核心的灵活拓展;基于“Spring+Hibernate”传统架构的J2EE应用关注功能逻辑过于业务逻辑,而Spring Cloud依据领域划分微服务,以领域模型替代“功能服务+数据表”模型,以并发的业务事件驱动替代传统消息总线数据驱动。基于微服务的工作流微服务组件设计延续了Spring Cloud“约定优于配置”的契约式编程思想,bootstrap.yml配置文件依据开发环境在spring.profiles.active配置项激活目标环境;Java持久层API(Java persistence API,JPA)将数据表名和字段名按照约定映射到方法类,构造面向对象的查询语句,以非侵入式设计灵活操作流程数据。
架构层面实现不同服务间功能解耦,工作流组件只需暴露API被其他服务调用,解决复杂业务逻辑的同时,在后续持续集成需求开发新业务功能时,可以在不影响业务逻辑和API原有设计前提下对工作流组件重新设计开发。工作流组件如表2所示。
表2 工作流组件概览
工作流组件运行的具体步骤如图4所示。
(1)流程管理器访问BPMN定义模型库,读取当前流程定义进入工作流引擎;
(2)由事务规则引擎驱动过程模型的启动和执行,事务规则引擎负责依据XML设计规范解析执行过程模型文件;依据事务规则库中的事务规则子模块对业务流转实现事务控制;
(3)Service API完成当前步骤并持久化流程数据,并且通过暴露的REST API被其他微服务调用;
(4)当前步骤执行完成后,工作流引擎处于等待状态直到流程监听器相应请求被触发。
图4 工作流组件运行示意
3.2 REST API与流程节点自由跳转设计
CMP中每个微服务可能是某个事件的生产者、消费者或二者兼有。当租户客户端提交云资源订单申请,租户服务端微服务组件作为当前事件的消费者向工作流组件REST API发送HTTP请求传入流程参数;工作流组件作为当前事件的生产者,调用业务层nextStep()方法类,完成持久化和参数传递驱动当前流程向下推进直到完成当前事件;工作流又作为当前事件的消费者,完成当前事件过程中向OpenStack-MS微服务组件REST API发送HTTP请求;OpenStack组件以生产者身份参与当前事件,依据API参数分配云硬盘、云主机等云资源。
基于微服务架构的工作流组件将传统工作流系统中基于每项具体业务功能设计的调用API设计为“下一步”(nextStep())、“回退”(rollback())、“开始”(startProcess())和“结束”(finishProcess())四个REST API,屏蔽每次请求的具体业务功能细节;基于Java泛型设计的动态实体类满足无状态JSON格式传入参数并将其代入流程参数传递。如订单审批工作流被调用startProcess()方法类的同时,会从API参数获取到诸如订单主题、备注、审批需求等订单信息传入当前流程实例的流程参数在流程中传递;针对特性需求,基于Java的继承特性,使得子类继承基类,根据事务需求灵活扩展开发。针对流程节点的自由跳转的业务需求,预设流程输出流程不能很好满足,以下伪代码实现了对引擎底层未暴露的Command API二次调用。
算法:rollback()两步回退算法。
(当前节点CurrAct;当前节点出方向OutgoingTrans;当前节点出方向列表OutgoingTransList;当前节点原始出方向OriOutgoingTrans;当前节点入方向CurrTrans;当前节点入方向列表CurrTransList;上一步节点PreAct;上一步节点入方向PreTrans;上一步节点入方向列表PreTransList;上两步节点MultiPreAct;当前节点新出方向NewTrans;当前节点新出方向列表NewTransList;)
for(OutgoingTrans:OutgoingTransList){
OriOutgoingTrans=OutgoingTrans;}
OutgoingTransList.clear();
for(CurrTrans:CurrTransList){
PreAct=CurrAct;
for(PreTrans:PreTransList){
MultiPreAct=PreAct;
NewTrans.setDestination(MultiPreAct);
NewTransList.add(NewTrans);
taskService.complete();
historyService.deleteHistoricTaskInstance;}}
NewTransList.clear();
OutgoingTransList.add(OriOutgoingTrans);
3.3 命令查询职责分离模式数据操作
传统单体架构应用采用CRUD设计模式(Create/Retrieve/Update/Delete,“增加/读取/更新/删除”),持久层操作和查询一种类型的数据通过相同的实体类。微服务架构系统要求业务逻辑的实现从数据驱动(Data-Driven)转向任务驱动(Task-Driven)和事件驱动(Event-Driven)。工作流微服务组件采用“命令查询职责分离”(command query responsibility segregation,CQRS)设计模式,为了使数据操作Command(会修改系统状态的增删改操作)和数据查询Query(不会修改系统状态的查询操作)分离。图5展示了CQRS与CRUD模式的异同。
涉及的CQRS中所有涉及到对数据库的改变均通过发送Command异步触发对应事务性操作,避免了CRUD中操作和查询同时进行可能导致的事务冲突;此外面向切面编程思想,通过添加“@Transactional”注解实现每个方法类粒度上的事务管理,从而最大限度保证对数据库事务操作的原子性和一致性;针对CMP资源订单的实时流程状态和历史流程状态进行列表分页展示的需求,基于CQRS设计模式,针对实时流程数据和历史流程数据设计独立的服务调用REST API,被请求时根据请求参数诸如租户ID、流程ID、执行步骤、关键词等列表分页展示条件查询或精确查询结果。
图5 CQRS与CRUD模式的异同示意
4 结束语
文中设计了CMP工作流微服务组件,发挥Activiti工作流引擎整合Spring Cloud微服务框架的原生优势;系统内API调用采用REST风格设计,最大限度上保证无状态请求的效率最大化,降低开发难度;流程节点自由跳转有助于有效完成工单订单审批流程在复杂业务场景下的特性需求;CQRS数据操作方案旨在解决复杂业务场景下功能逻辑的高耦合和低效操作下可能产生的误操作和资源浪费。CMP在OpenStack Pike版本云环境上验证了其在云资源调度管理上的可行性。开发改进方面,可以通过容器部署微服务应用[15],配置文件采取集约管理,以GitServer的方式按照不同开发环境依赖要求,实现配置文件集群部署。