4、springboot2.0整合工作流activiti6.0以及与业务集成时的一些坑
画图时将一个跳转条件${hr==2}写成了${hr=2}现在不能办理了!
如何删除这个任务呢!
runtimeService.deleteProcessInstance(procInsId, "xxx原因");
历史节点表( act_hi_actinst ):历史活动信息,这里记录流程流转过的所有节点。act_hi_taskinst表中只记录了历史节点表中节点类型为usertask的信息。
运行时流程执行实例表( act_ru_execution ):
运行时任务节点表( act_ru_task )
Activiti
是一个用Java编写的开源工作流引擎,可以执行BPMN 2.0
中描述的业务流程。Activiti
是Alfresco
的Alfresco Process Services (APS)
的基础,而Alfresco
是Activiti
项目的主要赞助商。
本文旨在帮助读者理解Activiti的工作机制,使其可以迅速上手该框架。本文将从这几个方面简单介绍了Activiti工作流引擎:
1、为什么要使用工作流引擎
2、BPMN2.0规范简介
3、开源BPMN项目对比
4、Activiti核心API
5、常见API调用步骤
1. 为什么要使用工作流引擎
假定我们有一个支付订单状态需要维护,它的状态图如下:
simple-state
它的状态跃迁自左向右,清晰名了,而且没有处理角色的概念,此时我们使用代码控制好状态流转即可,无需使用框架。
再来看另外一个场景,假定我们有一个企业内部采购订单,它的状态图如下:
stock-state
这个采购订单的状态复杂多变,状态的转换不稳定性很强,随时有可能增加新的状态;而且不同状态的处理人也是不同的,存在权限管理功能,若此时我们仍然使用一个状态字段来维持状态变更,无疑会困难重重。
工作流引擎就是为了解决这类问题而生的,我们可以观察当前实体(如支付订单、采购订单)是否具有如下特性,由此来确定是否需要引入工作流引擎。
- 状态的个数及其稳定性,个数多且不稳定,适合使用工作流引擎。
- 每个状态的处理人,处理人角色多且不稳定,适合使用工作流引擎。
工作流引擎实际上是放大了状态管理的功能,它根据既有流程图(基于BPMN2
规范)的指示,指定每一次状态跃迁的处理角色,在状态变更时持久化评论、表单、附件等数据,保存了完整处理轨迹。
工作流引擎 vs 规则引擎
- 工作流更像是管理状态跃迁的,规则引擎不关心状态跃迁,它关注的是处理过程中复杂条件的组合。
- 工作流引擎中包含“人”的任务,天生包含处理人角色控制;规则引擎不关心“人”的任务,不做特殊区分。
- 工作流引擎是宏观控制、规则引擎是微观控制。
常有人拿这两块内容做对比,笔者理解他们的侧重完全不同,没有太大的可比性。
2. BPMN2.0规范简介
业务流程模型和标记法(BPMN, Business Process Model and Notation
)是一套图形化表示法,用于以图形的方式详细说明各种业务流程。
它最初由业务流程管理倡议组织(BPMI, Business Process Management Initiative
)开发,名称为”Business Process Modeling Notation
”,即“业务流程建模标记法”。BPMI
于2005年与对象管理组织(OMG, Object Management Group
)合并。2011年1月OMG
发布2.0
版本(时至今日,没人会用1.0
版本了),同时改为现在的名称。
BPMN2.0
规范的实现,实质上是一个按照特定规范编写的XML
文件,使用特定的BPMN
设计器,即可以图形化的形式查看和编辑该文件。Activiti
以代码的形式实现了这套图形化表示法,使任务的流转依赖图形,而非具体的实现代码。
UML vs BPMN
UML和BPMN之间唯一的正式关系是OMG维护两个开放标准。
UML(统一建模语言)作为一种可视化的建模语言,其中的活动图也适用于流程建模,但其支持深度不够。
BPMN诞生时间晚于UML,据称从某种意义上讲,UML Activity Diagrams是BPMN的一个子集,也是BPMN的历史前身。
bpmn-structure
如上图所示,BPMN2.0规范包含了三个部分Gateway(网关)
、Event(事件)
、Activities(活动)
。
下面我们通过一个简单的流程定义文件来理解BPMN2.0规范。读者也可以访问这个在线设计站点来加速理解。
simple-bpmn-case2
上图是通过BPMN
设计器设计出来的简单流程,使用文本编辑器打开这个后缀为bpmn
的文件,得到如下内容(点击链接查看完整文件)。可以发现BPMN2.0
规范包含了三个部分在文件中都有体现:
- Gateway(网关):exclusiveGateway-排他网关,在做判断时使用,除了排他网关还有几个其它类型的网关。
- Event(事件):startEvent-开始事件、endEvent-结束事件,规范要求一个完整流程图必须包含这两个部分。
- Activities(活动):task-任务、sequenceFlow-连接线,活动是流程的主体部分,内部包含的类型相对较多。
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_0pbqtyh" targetNamespace="http://bpmn.io/schema/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="3.2.1">
<bpmn:process id="Process_0dechmy" isExecutable="false">
<bpmn:startEvent id="StartEvent_1jlefdo" name="开始">
<bpmn:outgoing>SequenceFlow_10f0wz2</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="SequenceFlow_10f0wz2" sourceRef="StartEvent_1jlefdo" targetRef="Task_19nyecg" />
<bpmn:sequenceFlow id="SequenceFlow_0jyecs7" sourceRef="Task_19nyecg" targetRef="Task_144tinh" />
<bpmn:exclusiveGateway id="ExclusiveGateway_0etavhv">
<bpmn:incoming>SequenceFlow_14jzbq9</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1kxhbdq</bpmn:outgoing>
<bpmn:outgoing>SequenceFlow_18w6f66</bpmn:outgoing>
</bpmn:exclusiveGateway>
<bpmn:sequenceFlow id="SequenceFlow_14jzbq9" sourceRef="Task_144tinh" targetRef="ExclusiveGateway_0etavhv" />
<bpmn:sequenceFlow id="SequenceFlow_1kxhbdq" name="通过" sourceRef="ExclusiveGateway_0etavhv" targetRef="Task_1m6ft2w" />
<bpmn:endEvent id="EndEvent_1xqrowc" name="结束">
<bpmn:incoming>SequenceFlow_1i6l681</bpmn:incoming>
<bpmn:incoming>SequenceFlow_18w6f66</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="SequenceFlow_1i6l681" sourceRef="Task_1m6ft2w" targetRef="EndEvent_1xqrowc" />
<bpmn:sequenceFlow id="SequenceFlow_18w6f66" name="拒绝" sourceRef="ExclusiveGateway_0etavhv" targetRef="EndEvent_1xqrowc" />
<bpmn:userTask id="Task_19nyecg" name="申请请假">
<bpmn:incoming>SequenceFlow_10f0wz2</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_0jyecs7</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Task_144tinh" name="领导审批">
<bpmn:incoming>SequenceFlow_0jyecs7</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_14jzbq9</bpmn:outgoing>
</bpmn:userTask>
<bpmn:userTask id="Task_1m6ft2w" name="休假">
<bpmn:incoming>SequenceFlow_1kxhbdq</bpmn:incoming>
<bpmn:outgoing>SequenceFlow_1i6l681</bpmn:outgoing>
</bpmn:userTask>
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<!-- 篇幅问题,省略了图形坐标相关的内容 -->
</bpmndi:BPMNDiagram>
</bpmn:definitions>
3. 开源BPMN项目对比
BPMN2.0
规范目前已成为流程处理事实上的标准,实现该规范的常见开源项目有这三个:jBPM
,Activiti
,Camunda
。
他们实现的功能比较相似,源头上它们存在递进关系:jBPM
–> Activiti
–> Camunda
。jBPM
是最早诞生的,Activiti
的发起人是从jBPM
项目中脱离出来的,Camunda BPM
的发起人是从Activiti
项目中脱离出来的。之所以存在三个同源项目,是由于开发者们对工作流引擎的期望存在分歧。
从技术组成来看,Activiti
最大的优势是采用了PVM(流程虚拟机)
,支持除了BPMN2.0
规范之外的流程格式,与外部服务有良好的集成能力,延续了jBPM3
、jBPM4
良好的社区支持,服务接口清晰,链式API更为优雅;劣势是持久化层没有遵循JPA规范。
jBPM
最大的优势是采用了Apache Mina
异步通信技术,采用JPA/JTA
持久化方面的标准,以功能齐全的Guvnor
作为流程仓库,有RedHat
的专业化支持;但其劣势也很明显,对自身技术依赖过紧且目前仅支持BPMN2
。
至于Camunda BPM 7
战略目标是“开发者友好”,jBPM
则致力于“零代码”的思想,而Camunda BPM
与Activiti
的区别零碎且不明显。
项目名称 | 企业 | 开始时间 | 开源方式 | 部署方式 |
---|---|---|---|---|
jBPM | Red Hat | 2006 | 社区版和企业版相同 | 支持嵌入式和独立部署 |
Activiti | Alfresco | 2010 | 社区版和企业版不同 | 支持嵌入式和独立部署 |
Camunda BPM | Camunda | 2012 | 社区版和企业版不同 | 支持嵌入式和独立部署 |
事实上三者的区别非常多,但随时时间的推移和版本迅速迭代,很多功能存在重叠,现在很难说哪个项目更强一些。当然,Camunda BPM出现时间最晚,社区也比较有活力,有文章(5 Reasons to switch from Activiti to Camunda)声称其比Activiti更具优势。
Activiti的发展历史:
- 2010年3月,jBPM的两位主要开发人员Tom Baeyens和Joram Barrez 离开了Red Hat,并成为了Alfresco员工的
Activiti
。Activiti
基于他们使用jBPM的工作流程经验,但它是一个新的代码库,不基于任何以前的jBPM
代码。
Activiti
的第一个版本是5.0
,表明该产品是他们通过jBPM 1到4
获得的经验的延续。
-
2016年10月,Barrez,Rademakers(Activiti in Action的作者)和其他贡献者离开了Alfresco。离职的开发人员分叉了Activiti代码,开始了一个名为Flowable的新项目。
-
2017年2月,
Activiti的
新商业版本发布并更名为Alfresco Process Services
。 -
2017年5月,
Activiti
发布了6.0.0版本,对ad-hoc子流程
和新的应用程序用户界面提供了新的支持。 -
2017年7月,
Activiti
发布了7.x版本, 向微服务架构迈进,进行大规模设计升级;可以与Spring Cloud
生态轻松集成。
4. Activiti核心API
Activiti
中包含了几个核心的Service
接口,它们是开发者调用Activiti API
的入口。
名称 | 说明 |
---|---|
ProcessEngine | 流程引擎,可以获得其他所有的Service。 |
RepositoryService | Repository中存储了流程定义文件、部署和支持数据等信息;RepositoryService提供了对repository的存取服务。 |
RuntimeService | 提供启动流程、查询流程实例、设置获取流程实例变量等功能。 |
TaskService | 提供运行时任务查询、领取、完成、删除以及变量设置等功能。 |
HistoryService | 用于获取正在运行或已经完成的流程实例的信息。 |
FormService | 提供定制任务表单和存储表单数据的功能,注意存储表单数据可选的功能,也可以向自建数据表中提交数据。 |
IdentityService | 提供对内建账户体系的管理功能,注意它是可选的服务,可以是用外部账户体系。 |
ManagementService | 较少使用,与流程管理无关,主要用于Activiti系统的日常维护。 |
完成一次流程的处理,常见步骤以及他们使用的Service如下图所示:
activiti-flow
现在再来介绍一些常见概念,在这些概念共同配合协作下,工作流引擎得以发挥出巨大威力:
4.1 流程 & 流程实例
流程由遵守BPMN2.0
规范的xml文件指定,定义流程即完成流程文件的设计。
流程发布后,使用RuntimeService
可以开启一个流程实例,每个流程可以开启N次流程实例,且实例之间的数据相互隔离。
4.2 用户任务
用户任务是BPMN2.0
规范中Activities(活动)
组件下的重要组成部分,在Activiti
中对应Task
类;区别于其他类型的任务,用户任务需要进行领取操作,不会自动执行,且领取从待处理任务列表中移除,其他候选人不可见。
4.3 用户 & 角色
Activiti
中内建了一个简单的账户体系,用户和角色是多对多的关系;IdentityService
中提供了对用户、角色操作的API。
另外,用户、角色与任务的联系,仅仅通过userId或groupId
,不要求必须使用内建账户体系;由于内建的过于简单,开发者完全可以使用自有的账户体系。
4.4 受让人、候选人、候选组
对用户任务做领取操作(claim
),即指定了该任务的受让人,每个任务只能有一个受让人,不能多次领取(但可以再次转让)。
任务的候选人和候选组支持配置多个,目的是指定处理该任务的人,不在候选列表中的人不允许处理该任务。另外,候选人、候选组可以流程文件中指定,也可以在监听事件中动态指定。
4.5 变量
Activiti
支持以key/value
的形式,对变量做持久化处理。变量通常有两个重要作用:
1、存储一些跟流程相关的业务数据,例如处理任务时提交的表单数据
2、流程定义文件中,可以通过UEL表达式获取存储的变量,例如,在互斥网关中选择正确的传出顺序流。
UEL表达式
UEL是java EE6规范的一部分,UEL(Unified Expression Language)即统一表达式语言,Activiti支持两个UEL表达式:UEL-value和UEL-method。
从类别上讲,变量可以分为三类:
名称 | 是否持久化 | 方法名 | 说明 |
---|---|---|---|
流程变量 | 是 | setVariable | 跟随流程实例,当前流程实例共享流程变量。 |
本地变量 | 是 | setVariableLocal | 跟随活动节点,不同节点之间不共享变量。 |
流程瞬时变量 | 否 | setTransientVariable | 跟随流程实例,只能在下一个“等待状态”之前访问它,“等待状态”表示当前流程实例中数据持久化的点。 |
流程本地变量 | 否 | setTransientVariableLocal | 跟随活动节点,只能在下一个“等待状态”之前访问它,“等待状态”表示当前流程实例中数据持久化的点。 |
注意
TaskService的setVariableLocal方法签名如下
void setVariable(String executionId, String variableName, Object value)
该方法传入了任务的executionId作为参数,但它存储的仍然是流程变量;流程变量还是本地变量是通过方法名称确定的,与使用RuntimeService还是TaskService没有关系。
4.6 表单
用户处理任务时,通常需要填写备注说明等表单数据,Activiti
的FormService
对此提供了支持,表单实现如下三种可选的方式:
名称 | 开启方式 | 数据存储位置 | |
---|---|---|---|
动态表单 | 流程定义文件中的activiti:formProperty属性 | 与变量一样,以key/value的形式存储在变量表 | |
外置表单 | 流程定义文件中的activiti:formkey属性 | 与变量一样,以key/value的形式存储在变量表 | |
普通表单 | 脱离Activiti掌控,开发人员自行创建表单和数据表,并使表单和任务关联即可 | 任意位置 |
三种方式中,动态表单由于无法指定样式,使用场景不多;外置表单的赋值和提交都依托Activiti引擎。
在此,笔者建议使用第三种方式普通表单,它的页面渲染赋值都由个人掌控,Activiti仅负责流程流转相关工作,页面渲染部分保持独立会使结构更清晰。
4.7 监听器
任务执行时,开发者常常需要触发一些自定义的动作,如动态分配候选人、任务结束时发送通知等;Activiti为开发者提供了两种方式来满足此类需求。
4.7.1 执行监听器(Execution listener)
执行侦听器意味着侦听一组有限的流程执行操作,如start、end和take
,开发者可以在启动或结束之前添加一些特定的业务逻辑。执行监听器需要实现ExecutionListener
或TaskListener
。
在流程文件中使用activiti:executionListener
标签,指定具体的监听类,如下:
xml
<!-- 1. 执行监听器的三种指定方式 -->
<activiti:executionListener class="org.activiti.examples.bpmn.executionlistener.ExampleExecutionListenerOne" event="start" />
<activiti:executionListener expression="${myPojo.myMethod(execution.event)}" event="start" />
<activiti:executionListener delegateExpression="${testExecutionListener}" event="start">
<!-- 2. 任务监听器的三种指定方式 -->
<activiti:taskListener event="create" class="org.activiti.MyTaskCreateListener" />
<activiti:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
<activiti:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
4.7.2 事件监听器(Event Listener)
事件监听器可以监听Activiti
引擎抛出的一组大型事件,这些事件级别较低,类型非常丰富,触发次数也较多。
创建ProcessEngine
时,可以通过eventListeners
属性指定事件监听器(也可以运行时通过RuntimeService.addEventListener
的方式添加),事件监听器需要实现ActivitiEventListener接口;每当流程实例产生变化时,监听器都能得到通知消息,点击事件类型列表查看所有通知类型。
使用这种方式引入的监听器,可以与流程定义文件解耦,是流程文件不再依赖Java代码。另外,事件监听器也支持在流程定义文件中声明,格式如下:
xml
<activiti:eventListener class="org.activiti.engine.test.MyEventListener" />
<activiti:eventListener delegateExpression="${testEventListener}" events="JOB_EXECUTION_SUCCESS,JOB_EXECUTION_FAILURE" />
执行监听器 vs 事件监听器
二者都可以对活动节点进行监听,执行监听器粒度较大,事件监听器粒度较小。出于便于维护的目的,笔者建议使用事件监听器,将事件监听和流程文件分开管理。
5. 常见API调用步骤
注意
本节示例代码,全部是基于Activiti 6.0.0版本的。
5.1 画流程图
流程图本质是一个符合BPMN2.0规范的xml文件,由拖拽式的设计软件完成,这里推荐几个:
BPMN2 Modeler:Eclipse插件。
BPMN diagram:一个在线编辑器。
Yaoqiang BPMN Editor:Java编写的客户端。
5.2 部署流程
Deployment deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
如果Activiti和Spring集成后,activiti-spring
提供了启动服务时自动部署流程的功能,它将在启动时检查流程文件是否有更新,以此决定是否再次部署。
5.3 开启流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
processDefinitionKey
:流程定义文件中定义的文件唯一标识。
businessKey
:参数为外部业务实体的唯一标识,比如采购订单的订单编号。
variables
:以Map的形式传入一组流程实例。
5.3 查询待办任务
List<Task> list1 = taskService.createTaskQuery().taskCandidateOrAssigned(userId).list();
List<Task> list2 = taskService.createTaskQuery().taskCandidateGroup(groupId).list();
List<Task> list3 = taskService.createTaskQuery().or().taskCandidateOrAssigned(userId).taskCandidateGroup(groupId).endOr().list();
taskCandidateOrAssigned
: 查询候选人或受让人是指定userId的任务
taskCandidateGroup
:查询候选组是指定groupId的任务
or和endOr
:查询候选人或受让人是指定userId、或选组是指定groupId的任务;在or和endOr之间的条件,将被“或”分割。
注意使用taskService查询得到的Task,都是未完成的,任务一旦完成就不能再通过taskService查询了,应改用HistoryService
。
5.3 认领并处理任务
taskService.claim(taskId, userId);
指定用户认领指定任务,即指定了该任务的受让人(Assignee)。
认领后该任务再使用taskService的taskCandidateUser
查询就查询不到了,只能根据taskAssignee
进行查询;这就是所谓对受让人之外的其他人不可见。
5.3 查询历史数据
List<HistoricActivityInstance> list1 = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().list();
List<HistoricTaskInstance> list2 = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceId().list();
createHistoricActivityInstanceQuery
: 查询流程下所有的活动,可以在这个查询中获得一个流程实例的完整轨迹。有别于BPMN2.0
规范中的Activities
,它既包含了任务(Task)
、也包含了网关(Gateway)。
createHistoricTaskInstanceQuery
: 查询流程下所有的任务,可以在这个查询中获得一个流程实例中所有的任务。
6. 小结
笔者在刚刚学习Activiti时,在一些基础原理上困惑了一段时间,事实上只要理解了工作流引擎的大致工作流程,再回过头来熟悉Activiti提供的API,无疑会容易理解许多,这也是笔者写这篇入门贴的初衷。
另外,本文示例代码片段大多摘自官方示例,对于想要直接看Example的读者,可以查看前辈咖啡兔的kft-activiti-demo或官方示例。
参考的文章
Activiti User Guide
AWS BPMN2 Process参考指南
BPMN FAQ – What Is the Relationship Between BPMN and UML?
BPMN (Business process modeling and notation) vs UML
Comparing and Contrasting Open Source BPM Projects
Camunda BPM 7 compared to JBoss jBPM 6
Activiti 7 Kick Off Roadmap
Activiti中三种不同的表单及其应用
Event Handlers Vs ExecutionListener
转自沉迷思考的鱼
工作流选型专项
1. 名词解释
1.1. BPM
Business Process Management,业务流程管理,“通过建模、自动化、管理和优化流程,打破跨部门跨系统业务过程依赖,提高业务效率和效果”。
1.2. BPMN
Business Process Modeling Notation,业务流程建模与标注,包括这些图元如何组合成一个业务流程图(Business Process Diagram);讨论BPMN的各种的用途,包括以何种精度来影响一个流程图中的模型;BPMN作为一个标准的价值,以及BPMN未来发展的远景。
1.3. BPEL
Business Process Execution Language,意为业务过程执行语言,是一种基于XML的,用来描写业务过程的编程语言,被描写的业务过程的每个单一步骤则由Web服务来实现。
1.4. XPDL
XML Process Definition Language,是由Workflow Management Coalition(简写为:WfMC)所提出的一个标准化规格,使用XML文件让不同的工作流程软件能够交换商业流程定义。XPDL被设计为图形上和语义上都满足交换用的商业流程定义,是描述BPMN图的最佳文件格式。BPEL也可以描述商业流程。但是XPDL不仅包含流程执行的描述,还包括了元素的图形信息,更适于商业流程建模。
1.5. JPDL
JBoss jBPM Process Definition Language,是构建于jBPM框架上的流程语言之一。在jPDL中提供了任务(tasks)、待处理状态 (wait states)、计时器(timers)、自动处理(automated actions)等术语,并通过图型化的流程定义,很直观地描述业务流程。
1.6. PVM
Process Virtual Machine,流程虚拟机,他的设计初衷是通过实现接口和定制插件等方式兼容多种流程定义语言和流程活动场景,为所有的业务流程定义提供一套通用API平台。那么,无论是需要对jBPM 原有流程定义语言进行扩展,或者重新实现一套专用的流程定义语言,都可以通过实现 PVM 指定的接口规范完成。
1.7. DMN
Decision Model and Notation,DMN的目的是提供一个模型决策结构,从而使组织的策略可以用图形清晰的地描绘出来,通过业务分析准确的定义,使其自动化(可选地)。
1.8. CMMN
Case Management Model and Notation,CMMN是一种图形化的符号,用于捕获工作方法,这些工作方法基于处理需要各种活动的情况,这些活动可能以不可预测的顺序执行,以响应不断变化的情况。通过使用以事件为中心的方法和案例文件的概念,CMMN扩展了可以用BPMN建模的边界,包括结构化程度较低的工作和由知识工人驱动的工作。结合使用BPMN和CMMN,用户可以涵盖更广泛的工作方法。
2. 众多工作流
2.1. JBPM(Java Business Process Management)
由JBoss公司开发,目前最高版本JPBM7,不过从JBPM5开始已经跟之前不是同一个产品了,JBPM5的代码基础不是JBPM4,而是从Drools Flow重新开始。下面要涉及的很多产品都是以JBPM4的代码为起点进行开发的。
2.2. Activiti
Alfresco软件开发,基于JBPM4,后并入OMG,目前最高版本activiti 7。Activiti5版本的时候,核心团队发生了比较大的变动(离职),activiti6的开发团队在新版本中去除了PVM,纳入了DMN,重构XML解析,BUG较多,目前主要团队致力于activiti7,5&6已经官宣不维护。
2.3. Osworkflow
完全用java语言编写的开放源代码的工作流引擎,具有显著的灵活性及完全面向有技术背景的用户的特点。由opensymphony组织维护,其不遵守XPDL等业务规范,完全使用XML编排业务。面向开发人员。
2.4. Shark
靠山是Enhydra。是一个可扩展的工作流引擎框架,它包括一个完全基于 WFMC 规范的标准实现,它使用XPDL(没有任何自己新的扩展)作为自身的工作流流程定义格式。其持久层和设计器都是自己公司研发的,持久层实现采用的标准是轻量级的Enhydra DODS O/R mapping 工具,设计器可以用Enhydra JaWE 图形XPDL编辑器。
2.5. Apache ODE
轻型的、可嵌入的组件,利用组件组装成一个完整的BPM系统。关键模块包括ODE BPEL编译器、ODE BPEL运行时、ODE数据访问对象(DAOs)、ODE集成层(ILs)和用户工具。虽然挂在Apache下面,但已经年久失修。
2.6. Flowable
基于activiti6,最新的开源版本是flowable6,开发团队是从activiti中分裂出来的,修复了一众activiti6的bug,并在其基础上研发了DMN支持,BPEL支持等等。相对开源版,其商业版的功能会更强大。
2.7. Camunda
基于activiti5,所以其保留了PVM,最新版本Camunda7,开发团队也是从activiti中分裂出来的,发展轨迹与flowable相似,同时也提供了商业版。
2.8. JFlow
前身ccFlow,国产的工作流引擎,由济南驰骋公司开发维护,主打中国式的业务流程,由于是国产的软件,中文化程度比较深,业务开发也对用户比较友好。国产的开源工作流引擎还是挺多的,JFlow是其中功能比较完善的一个,同时对比activiti,流程上更加中国化,支持自定义流程跳转,加签等。其他国产工作流就不列举了。
还有很多工作流,比如ProcessMaker,SWF,oracle,Bonita,openwebflow,snaker等,不过做BPM的话,相对于上面列举的产品还是有些缺陷,比如流程过于简单,资料过少等。
3. 关于工作流标准
BPMN是听得比较多的工作流标准,但工作流的规范其实不止一种,还有XPDL,BPML等。甚至他们的出现时间比BPMN更早,只是因为一些技术和非技术原因,BPMN2.0被普遍使用了,而非BMPN2.0规范的厂商也逐渐转移了。
以下的内容是关于规范标准之争中,BPMN2.0如何从众多规范中战胜并被普遍使用的。
3.1. BPMN1.X
在BPMN1.X里,BPMN是Business Process Modeling Notation的缩写,即业务流程建模符号,而在BPMN2.0里,BPMN变成了Business Process Model And Notation的缩写,即业务流程模型和符号,一个单词的增加却标示着BPMN本身发生了巨大的变化。
其中BPMN1.0在2004年5月由BPMI组织正式发布。这个阶段WSFL和BPEL-WS都已经被发布。这三种规范中,BPMN1.0仅仅作为业务流程建模的一系列符号标准,对业务比较友好。厂商们认为统一的建模标准能够使他们围绕核心建模工具提供其他更多的价值,更加愿意接受BPMN。
但BPMN1.x只是一些建模符号,不支持元模型,不支持存储和交换,也不支持执行。2008年,BPMN1.1发布,但仍然存在这些对开发人员并不友好的缺点,XPDL、BPEL和BPDM围绕着BPMN1.x的存储、交换和执行,产生了新的竞争。
XPDL作为WfMC提出的流程定义语言规范,本身就是一个元模型,可以存储,并且具备执行语义,因此理论上来讲,将BPMN转换为XPDL就可以解决存储、交换和执行的问题。XPDL2.0于2005年10月发布,在规范里,WfMC直接将XPDL的目标定义为BPMN的XML序列化格式。2008年4月23日发布的XPDL2.1规范,直接支持BPMN1.1到XPDL2.1的转换。XPDL是面向图的,BPMN也是面向图的,因此BPMN到XPDL的转换有着天然的优势。如今有超过80个的不同公司的产品使用XPDL来交换流程定义,同时也有一些厂商在自己提供的BPMN工具中使用了XPDL作为交换和存储格式。
BPEL-WS规范在2003年4月提交给了OASIS(Organizationfor the Advancement of Structured Information Standards,结构化信息标准促进组织)并更名为WSBPEL(Web Services Business Process Execution Language)规范, 2007年4月发布WSBPEL2.0版本,除了Microsoft、 BEA、 IBM、 SAP 和Siebel,Sun Microsystems和甲骨文公司也相继加入了OASIS组织。除去政治因素,BPEL的流行还在于Web正成为分布式系统架构的平台以及SOA的雄起,SOA强调服务的分解和解耦,而BPEL则对这些WEB服务进行编制,两者密不可分。但BPMN到BPEL的转换存在着先天上的缺陷,原因是BPMN是基于图的,而BPEL是基于块的,BPEL是一个结构化(块[Block])和非结构化(控制链和事件)的混合体。这个缺陷导致有些BPMN建模的流程无法映射到BPEL,两者的双向工程更是存在问题。这个缺陷成为人们反复诟病的对象。许多支持BPEL的产品为了解决这一问题,不得不在用户建模时做出种种限制,让用户绘制不出无法转换的模型。
而BPDM(业务流程定义元模型,Business Process Definition Metamodel)则是OMG组织自己提出来解决BPMN存储和交换问题的规范。于2007年7月形成初稿,2008年7月被OMG最终采用。BPDM是一个标准的概念定义,用来表达业务流程模型。元模型定义了用来交换的概念,关系和场景,可以使得不同的建模工具所建模出来的流程模型进行交换。BPDM超越了BPMN和BPEL所定义的业务流程建模的要素,它定义了编排和编制。
3.2. BPMN2.0
BPMN2.0 beta1版本于2009年8月发布,BPMN2.0 beta2版本于2010年5月发布,BPMN2.0正式版本于2011年1月3日发布。BPMN2.0正式将自己更名为Business Process Model And Notation(业务流程模型和符号),相比BPMN1.x,最重要的变化在于其定义了流程的元模型和执行语义,即它自己解决了存储、交换和执行的问题,BPMN由单纯的业务建模重新回归了它的本源,即作为一个对业务人员友好的标准流程执行语言的图形化前端。BPMN2.0一出手,竞争就结束了,XPDL、BPEL和BPDM各自准备回家钓鱼。
BPMN2.0是最被广泛使用的标准,也是当前热门产品使用的标准,详情请看整理的表1:
工作流框架 | 遵循规范 | 备注 |
---|---|---|
Bonita BPM | XPDL | 流程过于简单 |
Shark | XPDL | 不维护-2017 |
Osworkflow | 自定义XML规范 | 不维护 |
JBPM | BPMN2.0 | JBPM4.3后添加了对BPMN的支持,持续开源 |
Apache ODE | WS-BPEL、BPEL4WS | 不维护 |
Activiti | BPMN2.0,XPDL,JPDL | Activiti7维护 |
Flowable | BPMN2.0,XPDL,JPDL | 持续开源 |
JFlow | BPMN2.0,Ccbpm | 2015年后为了与国际接轨,开发支持BPMN |
Camunda | BPMN2.0,XPDL,JPDL | 持续开源 |
表1
这个表格可以对一些工作流产品有一个初步印象。
4. 分表对比
4.1. 对比须知
为了方便查看汇总表格,有必要再深入展示几个开篇提到的概念:
PVM
PVM是在JBPM4的时候被纳入的,activiti5沿用,activiti团队在activiti6就已经移除了,ActivitiImpl, ProcessDefinitionImpl, ExecutionImpl, TransitionImpl 都不可用了。所有的流程定义有关的信息都可以通过BpmnModel来获得,通过org.activiti.engine.impl.util.ProcessDefinitionUtil来拿到BpmnModel。
工作流中,由于flowable是基于activiti6开发的,所以代码中也没有PVM,Camunda基于activiti5开发的,所以PVM还在,更改这个核心引擎没有绝对的好坏之分,但是由于我们的代码是基于activiti5开发的,所以对我们的代码移植是有影响的。
DMN
BPMN是OMG公司发布的工作流规范,而DMN同样是OMG公司发布规范,该规范主要用于定义业务决策的模型和图形,1.0版本发布于2015年,目前最新的是1.1版本,发布于2016年。
BPMN主要用于规范业务流程,业务决策的逻辑由PMML等规范来定义,例如在某些业务流程中,需要由多个决策来决定流程走向,而每个决策都要根据自身的规则来决定,并且每个决策之间可能存在关联,此时在BPMN与PMML之间出现了空白,DMN规范出现前,决策者无法参与到业务中。为了填补模型上的空白,新增了DMN规范,定义决策的规范以及图形,DMN规范相当于业务流程模型与决策逻辑模型之间的桥梁。
虽然DMN只作为工作流与决策逻辑的桥梁,但实际上,规范中也包含决策逻辑部分,同时也兼容PMML规范所定义的表达式语言。换言之,实现DMN规范的框架,同时也会具有业务规则的处理能力。
CMMN
CMMN具有与BPMN不同的基本范例。 CMMN没有顺序的流程。相反,它以某种状态对案例建模。根据状态,视情况而定,有些事情可能会处理,而有些事情可能不会。控制主要由人来执行。 CMMN是声明性的,该模型说明了要应用的内容,但没有说明如何实现它。相反,BPMN强制性地规定了流程中某些步骤必须进行的工作。对于大多数人而言,声明性建模更为复杂且较不直观。结果,CMMN模型更加难以理解。您不能简单地用手指追踪路径!
CMMN对可能的活动和活动限制进行建模。它对活动何时发生,何时必须发生以及何时不应该发生进行建模。 CMMN同样限制了流程中人员可以使用的操作范围。事例模型必须事先经过仔细考虑。重要的是要提出这一点,以应对人们经常误解的事实,即人们在案件管理方面可以做他们想做的任何事情。
CMMN和BPMN都描述了业务流程中的活动。这些标准之间的主要区别是:
1、BPMN采用绑定方法。 它提供了活动的确切顺序。提供自由度比较困难。比如加个节点、任意跳转就很麻烦。
2、CMMN采用非约束性方法,然后增加了限制。建立排序比较困难。
换句话说,原则上您可以用任何一种表示法表达大多数问题。但是,根据问题的类型,建模将在BPMN或CMMN中更好地工作,并且这些标准之一更可能产生整洁有效的模型。
使用CMMN的指标包括:
1、无需序列:如果序列无关紧要,并且可以按任何顺序执行任务,则这将在BPMN中产生过多的连接-临时建模。也许使用临时子流程可以避免混乱。
2、活动取决于条件:定义条件是CMMN的强项。可以定义许多任务,但是它们只能在某些情况下起作用。例如,这种情况可能是订单超过一定数量或客户具有VIP身份;其他已完成的任务也会影响条件。可选因素和数据相关因素的这种组合不能在BPMN中反映出来。
3、专用计划阶段:由于能够处理任意任务,CMMN可以适应一个计划阶段,在该阶段中,一个工人计划一个案例并启用任务。 其他工人将不得不遵守计划。 BPMN不能做任何事情。
包括BPMN标准,这三个标准都是由OMG提出的。多数机构认为DMN和CMMN是工作流发展趋势。
4.2. 对比表格
经过第二个章节的比较,我从支持的标准和社区活跃度表现比较好的工作流中筛选出几个选项进行进一步对比,如表2:
Activiti 7 | Flowable 6 | Camunda bpm | JBPM 7 | JFLOW(国产的) | |
---|---|---|---|---|---|
功能 | |||||
会签 | √ | √ | √ | √ | √ |
回退 | × | √ | √ | - | √ |
驳回 | × | √ | √ | √ | √ |
自定义流转 | × | × | √ | - | √ |
加签、减签 | × | √ | √ | - | √ |
多实例 | √ | √ | √ | √ | √ |
事务子流程 | √ | √ | √ | √ | √ |
版本迁移 | × | × | √ | × | × |
兼容性及二次开发 | |||||
支持的流程格式 | BPMN2.0、XPDL、PDL | BPMN2.0、XPDL、XPDL | BPMN2.0、XPDL、XPDL | BPMN2.0 | BPMN2.0 |
开源情况 | 开源 | 提供商业和开源版 | 提供商业和开源版 | 开源 | 开源 |
开发基础 | jBPM4 | Activiti 5 & 6 | Activiti 5 | 版本5之后Drools Flow | 自开发 |
直接支持的脚本 | JUEL、groovy | JUEL、groovy | python、ruby、groovy、JUEL | - | - |
引擎核心(跟代码兼容有关) | 去除PVM | 去除PVM | 流程虚拟机(PVM)(迁移上有优势) | Drools | 自研 |
Spring结合 | √ | √ | √ | √ | √ |
二次开发难度 | 一般 | 一般 | 一般 | 较难 | 一般 |
未来拓展 | |||||
CMMN支持 | × | √ | √ | × | × |
DMN支持 | √ | √(6.4之前不稳定) | √ | √ | × |
历史数据处理(NoSql) | × | √ | √(只提供了解决方案) | - | × |
支持数据库 | Oracle、SQL Server、MySQL | Oracle、SQL Server、MySQL、postgre | Oracle、SQL Server、MySQL、postgre | Mysql,postgre | oracle,sqlserver,mysql |
集群部署 | √ | √(6.5版本支持) | √ | √ | √ |
云部署 | √ | - | √ | - | √ |
其他特性 | |||||
持久化框架 | Mybatis | JPA二次封装 | Hibernate | JPA | - |
架构 | spring boot 2 | spring boot 1.5 | spring boot 2 | Kie | spring boot 2(特别版本) |
事务管理 | MyBatis机制/Spring事务控制 | hibernate机制/Spring事务控制 | hibernate机制/Spring事务控制 | Bitronix,基于JTA事务管理 | - |
分布式事务 | MyBatis机制/Spring事务控制 | - | 补偿机制,SAGA 模式 | Bitronix,基于JTA事务管理 | - |
开发手册 | https://activiti.gitbook.io/activiti-7-developers-guide/部分网页打不开 | http://www.shareniu.com/flowable6.5_zh_document/bpm/index.html | https://docs.camunda.org/manual/7.13/user-guide/ | https://docs.jboss.org/jbpm/release/7.40.0.Final/jbpm-docs/html_single/ | http://ccbpm.mydoc.io/ |
运行模式 | 独立运行和内嵌 | - | 独立运行和内嵌 | - | 独立运行和内嵌 |
源码活跃度 | 相对活跃 | 相对活跃 | 比较活跃 | 相对活跃 | 一般 |
源码地址 | https://github.com/Activiti/Activiti | https://github.com/flowable/flowable-engine | https://github.com/camunda/camunda-bpm-platform | https://github.com/kiegroup/jbpm | https://gitee.com/opencc/JFlow |
设计器 | 集成idea eclipse,web | 自提供,eclipse | 自提供,eclipse | Eclipse | 自提供,.net开发 |
集成接口 | SOAP、Mule、RESTful | SOAP、Mule、RESTful | SOAP、Mule、RESTful | 消息通讯 | SOAP、Mule、RESTful |
内部服务通讯 | Service间通过API调用 | Service间通过API调用 | Service间通过API调用 | 基于Apache Mina异步通讯 | - |
表2
特别说明:
-
源码活跃度:从分支数,提交数,参与者,最近提交时间等判断
-
Drools Flow (process/workflow):该工作流引擎是Drools下的一个项目,JBPM的规则引擎正是Drools,由于activiti开发自JBPM4,所以activiti,flowable以及Camunda都有Drools的影子。
-
空白处或者小短杆表示的代表的暂时未查证的内容
-
另外要说明的是,表格中支持的功能需要有不少部分需要认真探讨,比如Camunda宣称支持各种功能,以及Nosql存储,但实际上,其支持的回退,撤回都是通过一个跳转实现的,要打折扣,NoSql也只是提供了解决方案,要自己实现噢,后面一篇文章会再提及。
5. 性能
关于工作流性能比较的文章比较少(少得可怜),因为没有直接的数据能够对比工作流之间的性能,所以独立出一章介绍,基本情况:
5.1. 概述
以下内容来自:
据说是16年的一份科学性能报告,可惜性能报告中,除了Camunda外,其他两种被对比的WFMS产品名称并没有写出来,所以这个报告只能作为参考:
“In general, we may conclude that Camunda performed better and more stable for all metrics when compared with WfMS A and WfMS B.”
“WfMS A and Camunda share many architectural similarities because Camunda was originally a fork of WfMS A.” 暗指WfMS A是activiti。
为了得到更多的性能数据,接下来从各个官网寻找材料。
5.2. Camunda
https://camunda.com/products/performance/
该地址没有描述具体的性能,但是列举了一些措施,表示做了性能考虑:
-
紧凑型表:减少必要的存储数据,在最好的例子中,修改一个活动只需要更新一条数据
-
避免死锁:采用乐观锁;用户思考期间不持有锁;批量刷新数据
-
控制保存点:在一个事务中保存多个活动
-
智能缓存:使用一级缓存,减少查询
-
并行:并行任务在数据库中表现为不同行,实现真正并行
-
集群:多节点共用数据库
-
最小资源占用:流程引擎无状态,每个节点只需要分配少于10M的缓存,所以支持大批量任务在节点上运行
-
分库:历史库和运行库是分开的,原则上,历史数据可以转移到任何大数据产品上
5.3. Flowable
https://flowable.com/open-source/docs/bpmn/ch18-Advanced/#async-executor
这里没有特别介绍提升性能的设计,但一些角落有提到,对性能是否提升未知:
1.额外写了UUID id生成器,解决并发的bug,但其实不一定能提升性能;
2.数据库的批量插入
3.async executor:异步执行器,能解决背压,但是对性能的提升程度未知
5.4. JBPM7
https://jbpm.org/learn/jbpmPerformance.html
这里也没有介绍性能的具体情况。但提供了检测性能的方法,指明了不跟任何工作流作对比,言辞谨慎。官网给的例子仍然可以作为参考数据,翻译如下:
客户端:jmeter
版本:社区版 7.8
硬件:
Macos 10.14.4
Cpu i7 2.3GHZ
Memory 16GB
Db Postgre
测试内容:三个简单的流程,我做了简单的表格继承他的测试结果;(利用执行1000个实例用时得出TPS)
单位:TPS | 单线程 | 四线程 | 八线程 |
---|---|---|---|
简单脚本任务 | 91 | 240 | 361 |
简单用户任务 | 16 | 52 | 72 |
用户任务+并行脚本任务 | 11 | 45 | 70 |
5.5. Activiti 7
https://activiti.gitbook.io/activiti-7-developers-guide/
有提到一些提升查询性能的地方,但是不明确。
5.6. JFlow
未提及性能
6. 总结
大致总结以下调研的总体感受。Activiti7相对于5和6没有太多功能上的变化,主要致力于一些辅助功能,对接一些基础技术。比如云原生,ELK,spring cloud。分布式的应用或许会对性能有一定的提升。
Flowable的NoSql方案和消息队列比较特别,同时对DMN和CMMN的研究也比较多,是个不错的选择。
JBPM近年来新的文档少一些,应用和二次开发可能会比较吃力。JFlow功能比较齐全,而且中文化的设计器对开发人也和业务人也都比较友好,但是他的材料基本限于官网,后期不会保障。
Camunda BPM支持的功能比较多,对DMN和CMMN的支持也是推出最早的,性能上看起来也做了比较多的应对,虽然商业版的推出减少了开源版的维护,但仍然是几个竞品中综合看起来比较符合当前需求的,PVM的保留也会使得迁移比较顺滑,具体使用情况还需要进一步尝试,比较推荐。(如果不是旧产品迁移其实需要更多选择,框架改革的优化是可以考虑在内的,根据需求选择)