Activit简介
1. 业务背景
项目开发过程中如果均是简单的数据请求与返回,那么方法调用和业务逻辑是最容易处理的,根据入参返回数据即可,数据的生命周期始于请求,终于数据返回,没有其他。
倘若特定需求场景需要多个接口协作完成一件事,数据流转存在多路由,业务逻辑处理将会呈现复杂化,朴素的数据流控制方式就是定义数据中间态通过硬编码形式来影响数据流向,这种设计在复杂度不深的情况下总是很容易实现,开发成本和沟通成本较低,也不失为一种非常有效的开发设计方式。而随着业务场景复杂化,流程变更频繁,开发人员会在之前得益的简单设计上发现维护和可拓展性极差,甚至陷入流程泥潭中难以自救,最直接的表现就是接口交互定制化,所有的交互看不到任何业务或故事主线,所有的服务交互都需要最原始的那些开发人员的文档、注释甚至“言传身教”的指导才可以洞察复杂业务的其中一二,这是软件开发中的技术负债和不完善,我们急需一个可以引导完整业务流程的体系或者框架来引导服务交互,来驱动业务数据流转,对数据的出生、中转、停留及最终消亡进行有效控制和监管,让服务有源可溯,有序可遵。关于以上概述都是为了引申出下面项目实践的利器,工作流。
- 数据流转依赖硬编码,面向接口交互,没有统一司令塔服务进行调度
- 面向业务数据设计,复杂逻辑的业务中,数据流故事主线不清晰,无法监控也无从溯源
2.技术调研
JBPM vs Activiti选型对比
关于工作流开源框架,一般有JBPM和Activiti,简单检索了下两者对比如下:
Activiti持久层通过MyBatis实现、与Spring融合支持事务,与当前项目技术背景较为符合,且上手较为便捷,参考资料广泛,学习成本较低,加上之前个人项目运用过Activiti前身,对PVM设计模式有一定了解,最终决定采用Activiti作为工作流来进行开发。
Activiti工作流特点
关于Activiti工作流的具体内容这里不做赘述,本文的核心放在Activiti工作流与业务结合的实践,下面是Activiti工作流的一些特点:
- 数据持久化
- 支持链式API编程风格,所有的编程参与对象都可通过ProcessEngine获取到
- 支持流程设计器。可以结合IDEA中的ActiBpm等插件进行可视化流程设计,它最终转换的是bpm文件,是一个类xml的流程配置文件
- 原生支持Spring
- 分离运行时与历史数据
- Activiti是基于单库单表的持久化
3.流程设计
目前负责的项目是一个关于用户认证相关的业务,简单描述认证业务流程如下:
- 工作流起始 工作流的开始和结束,是整个工作流程的起点和重点
- 工作流节点 工作流的核心节点和衔接,每个节点是故事主线的主要构成部分,代表一个聚合的业务逻辑,每个节点根据预定义走向进行数据驱动
- 工作流路由 工作流路由分发,根据前置数据来进行工作流走向的决策,从而影响后续节点的流转
- 数据中间态 除了开始、结束两个节点,被虚线包括的部分都是数据的中间态,无论业务数据和工作流数据,此时呈现的最大特点就是数据的不稳定性,业务处理中随时都可能根据外部业务驱动产生数据和业务流程的向下继续、向上回溯、分叉决策等,也是工作流中最活跃的部分
4.架构设计
Service业务逻辑层
- Activiti Service 封装了工作流基础API,这里主要用到Activiti的RepositoryService、RuntimeService、TaskService三个服务类,RepositoryService提供了服务流程部署功能,RuntimeService提供了运行时服务这里主要涉及任务启动,TaskService提供了任务完成、任务取消等流程驱动的方法支持。这里是基于Activiti原生API进行了定制化任务支持,类似一个门面服务,主要是用来融合Activiti原生服务和实际项目业务需求的API
- WorkFlowAspect 通过Aop切面将工作流逻辑进行抽离,保证与业务层具体逻辑方法的隔离实现解耦;调用底层Activiti Service是提供的服务接口完成工作流驱动
- @WorkFlowHandle 通过Spel解析Annotation注解形参,支持Map类型复杂数据结构传参,结合Aop进行实参映射绑定,解决切面层与业务层参数传递问题;通过@Repeatable支持业务方法的重复注解,为工作流作用业务方法的灵活配置的可扩展提供支持
Dao数据持久层
- 水平拆分 根据业务数据的库表设计进行工作流持久化改造,按照用户userId进行工作流数据水平拆分
- 事务支持 由于切分键一致,使得工作流数据与业务数据分离的同时能够支持数据库事务,保证数据完整一致性,减少开发复杂度
5.项目实战
5.1 maven配置
目前Activiti已进入7.0.0+,翻阅了大量网上资料,该版本输出的时间较少,大部分还集中在5或6,这里基于可用可操作的思想选用了介绍参考资料详实的5.22.0进行开发,每个大版本变更差异较大,其他版本根据实践需要进行升级或取舍
5.2 持久化改造
分库分表
Activiti默认是单库单表的,而我们现有项目是分库分表的,路由策略是根据用户ID进行水平切分的。
对Activiti的持久化存储进行分库分表改造基于以下两点考虑:
- 业务数据分库分表,Activiti工作流数据的流转肯定是要和业务数据做联动的,按照同样语义即用户ID进行水平切换路由可以轻松实现数据库事务,减少实现数据一致性带来的设计问题。
- 业务表做了分库分表的原因就是用户体量大,持久化数据庞大,尽管读多写少且写入量单库单表在一段时间内不会对业务性能产生丝毫影响,但是为了扩展性更好,也为了规避后续变更难度,决定对Activiti工作流数据持久化也进行水平拆分。
字段及索引长度适配
- 为了保证数据完整性,Activiti默认创建表存在大量的外键约束,在生产环境下可以根据自身开发需要对这些外键进行去除,从而提高库表查询和处理性能。关于数据完整性可以通过应用程序层面进行保证和处理。
- Activiti的库表设计都是基于可用性和普遍适配性进行设计的,个别字段如存在varchar(4000)来记录异常信息,根据自身业务存储及性能考虑进行了部分字段基于可用性的缩减;还有部分表存在大量的联合索引,由于字段长度过长导致联合索引存储大于736字节,这是MySQL官方推荐的索引最大值,超过该值可能会产生性能问题,项目中没有对MySQL默认限制进行修改,而是根据项目情况缩减了联合索引字段的长度。
5.3 部署bpmn更新问题
在实际项目环境下,没有bpmn流程调整我们是不需要频繁进行bpmn流程部署的,每次新流程部署都会更新刷下流程ID、实例ID等,而且数据也会产生变更调整,而我们的需求是在需要更新的时候更新,不需要更新的时候复用即可。因此在服务启动时,我这里通过时间戳作为版本号,每次启动Spring服务时进行bpmn部署检查,如果应用中的版本号未进行变更则不刷新Activiti部署信息保持之前部署快照,否则创建并部署新bpmn流程进行部署创建,更新部署快照信息,下面放一段demo代码,由于项目是分库分表的,每次启动服务会轮询每个分库的deploy信息情况。
5.4 Activiti核心服务
Activiti主要有以下几个核心服务,我们这里最重要的是使用到了RepositoryService、RuntimeService、TaskService,RepositoryService用来进行服务部署,RuntimeService用来启动任务管理任务实例,TaskService用来进行任务的查询、流转(完成、取消)等操作。
5.5 整合Spring
与Spring整合后,可以通过Spring来操作配置Activiti
5.6 工作流常量定义
5.7 工作流注解@WorkFlowHandle & @WorkFlowHandles
@WorkFlowHandle是支持工作流操作的注解,@WorkFlowHandles则是借助JDK1.8的@Repeatable特性支持重复注解,使得@WorkFlowHandle可以重复作用在业务Service方法上进行工作流操作。
@WorkFlowHandle的定义如下:
注解字段 | 含义及作用 |
assignee | 任务委派人。工作流的流转节点需要指派任务委托人进行处理,这里用来指定任务由谁来处理 |
bizId | 业务ID。业务数据的唯一ID,以此来关联工作流数据做绑定,说白了就是通过bizId可以找到业务数据和工作流数据的对应关系,是二者交互的桥梁,bizId必须是全局唯一的 |
variables | 环境变量。Activiti工作流中支持了任务实例流转过程中任务变量的存储和绑定,这些环境变量可以用来进行工作流路由的决策和驱动工作流流转,Activiti支持局部变量和全局变量两种形式,一般我们使用全局变量 |
taskOperate | 任务操作类型。这里是根据我们业务情况定义的工作流具体操作,一般分为任务启动、任务完成、任务取消 |
cancelReason | 任务取消原因。仅在任务取消时使用,标记取消原因 |
node | 任务节点标记。相当于对当前任务节点的标记,更新和取消任务都要明确传入当前任务节点,当传入任务节点与实际当前任务节点不符时会抛出异常 |
这里的注解字段定义可以根据具体业务进行抽象和具体,以上仅供参考。
5.8 工作流切面 WorkFlowAspect
WorkFlowAspect是以切面形式存在的工作流业务,把涉及工作流相关的元素数据抽离到切面中进行集中处理,通过注册与业务逻辑进行融合,代码隔离减少了逻辑耦合和混杂,通过以下脚手架的拼接完成了该功能的实现,汇总如下:
元素 | 功能 | 解决问题 |
Aop | 切面 | 抽离工作流业务逻辑,与业务代码绝缘,避免逻辑耦合 |
@Repeatable | 可重复注解 | 单一方法可重复注解,扩展性更高。例如,可单独启动任务、启动并完成任务、启动并完成任务并取消其他任务等 |
Spel | 表达式解析 | 通过强大的解析器Context环境进行Annotation注解参数注入并解析,支持Map类型数据结构复杂传参,解决了业务方法入参数据与切面业务传递可共享 |
TransactionTemplate | 事务支持 | 由于切面包裹业务方法,同时监听业务方法和切面方法逻辑,实现数据的原子操作,天然的支持事务,保证数据和业务逻辑的完整一致性 |
5.9 工作流与业务Service融合
6. 其他
表删除顺序
由于存在外键,表删除需要按照以下顺序进行,第一遍没删除掉,可以再来一遍就干净了,测试环境使用的备记下
7. 参考资料
http://www.mossle.com/docs/activiti/index.html
https://gitee.com/jerryshensjf/ActivitiDemo/tree/master
https://cloud.tencent.com/developer/article/1584828
作者:大摩羯先生链接:https://juejin.cn/post/6925336703285051399来源:稀土掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Loading...