Activiti5入门--并行网关

紧接着上一章的排他网关,本章开始介绍并行网关(ParallelGateWay)。

和上一章一样,为了更好地讲解并行网关(ParallelGateWay),我们定义一个网购流程,该流程定义客户付款,商家发货,客户收货,商家收款四个用户任务。

在学习之前,我们创建新包cn.demo.parallelgateway及其对应的类ParallelGateWay,并新建流程图文件

parallelGateWay.bpmn

定义网购流程

现在,我们打开parallelGateWay.bpmn,开始定义网购流程

  1. 创建一个开始节点和一个结束节点
  2. 创建四个用户任务节点,从左到右,从上到下,分别为usertask1、usertask2、usertask3、usertask4
    • 设置usertask1的Name值为付款,Assigee的值为买家
    • 设置usertask3的Name值为收款,Assigee的值为商家
    • 设置usertask2的Name值为发货,Assigee的值为商家
    • 设置usertask4的Name值为收货,Assigee的值为买家
  3. 在开始节点和用户任务节点,结束节点和用户任务节点之间分别添加一个并行网关,前者用于分支,后者用于聚合
  4. 分别连接各节点,形成两个并行的分支
    • 开始-并行网关-付款-收款-结束
    • 开始-并行网关-发货-收货-结束
  5. 点击空白处,设置Id的值为parallelGateWay,其Name值为parallelGateWayProcess

当完成上述步骤以后,整个流程就定义好了,其流程图如下

并行网关

可以看到,该流程存在分支并聚合的现象

并行网关

部署流程定义

和前面一样,我们首先要做的依然是部署流程定义以及启动流程实例。先来部署一下流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 部署流程定义
*/
@Test
public void deploymentProcessDefinition() {
InputStream inputStreamBpmn = this.getClass().getResourceAsStream("parallelGateWay.bpmn");
InputStream inputStreamPng = this.getClass().getResourceAsStream("parallelGateWay.png");
Deployment deploy = processEngine.getRepositoryService() // 与流程定义和部署对象相关的Service
.createDeployment() // 创建一个部署对象
.name("并行网关") // 添加部署的名称
.addInputStream("parallelGateWay.bpmn", inputStreamBpmn)
.addInputStream("parallelGateWay.png",inputStreamPng)
.deploy(); // 完成部署
System.out.println("部署ID: " + deploy.getId());
System.out.println("部署名称: " + deploy.getName());
}

运行代码,部署成功的话,将输出如下结果

部署ID: 4101
部署名称: 并行网关

启动流程实例

紧接着部署流程定义之后,理所当然地,我们要启动流程实例

1
2
3
4
5
6
7
8
9
10
11
12
/**
* 启动流程实例
*/
@Test
public void startProcessInstance() {
// 流程定义的key
String processDefinitionKey = "parallelGateWay";
ProcessInstance processInstance = processEngine.getRuntimeService()
.startProcessInstanceByKey(processDefinitionKey);
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId());
}

运行代码,如果启动成功,则会得到如下结果

流程实例ID: 4201
流程定义ID: parallelGateWay:1:4104

流程分析

分析并行网关的流程图可以知道,当流程实例启动以后,流程会执行到并行网关节点,但是并行网关没有任何操作,所以流程继续往下执行。

下面,我们来分析并行网关执行过程中的数据库表变化。

正在执行的执行对象表

首先,执行SQL语句SELECT * FROM act_ru_execution,查询act_ru_execution(正在执行的执行对象表),可以看到,数据库表中存在三条记录

并发网关-正在执行的执行对象

注意字段PROC_INSTID(流程实例ID)和字段ID_(执行对象ID),可以看到,存在一条ID_PROC_INSTID

相等的记录,表明这是一条流程实例记录,而另外两条记录的ID_PROC_INSTID则不相等,这些是执行对象的记录。因此,在并行网关中,若计算正在执行的对象记录数,应该在并行分支数的基础上加一条。

正在执行的任务表

接下来,我们执行SQL语句SELECT * FROM act_ru_task,查询act_ru_task(正在执行的任务表),可以看到,和以往同一时间只有一条记录不同,该表同时存在两条记录,即一个流程实例同一时间存在两个正在执行的任务。

正在执行的任务1

正在执行的任务2

所有活动节点的历史表

现在,我们来看看所有活动节点的历史表,执行SQL语句SELECT * FROM act_hi_actinst,可以看到,包括并行网关在内的所有节点总共有四个,其中开始节点和并行网关节点已经结束,而两个任务节点还没有结束

所有历史活动节点

所有历史活动节点1

为了进一步了解该表的数据变化,我们通过查询act_ru_task依次先完成付款、发货、收货这三个任务

  1. 完成付款任务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 完成我的任务
    */
    @Test
    public void completeMyPersonalTask() {
    String taskId = "4207";
    // 完成任务的同时,设置流程变量message的值,来控制下一连线
    processEngine.getTaskService().complete(taskId);
    System.out.println("完成任务:任务Id:" + taskId);
    }
  2. 完成发货任务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 完成我的任务
    */
    @Test
    public void completeMyPersonalTask() {
    String taskId = "4210";
    // 完成任务的同时,设置流程变量message的值,来控制下一连线
    processEngine.getTaskService().complete(taskId);
    System.out.println("完成任务:任务Id:" + taskId);
    }
  3. 完成收货任务

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /**
    * 完成我的任务
    */
    @Test
    public void completeMyPersonalTask() {
    String taskId = "4402";
    // 完成任务的同时,设置流程变量message的值,来控制下一连线
    processEngine.getTaskService().complete(taskId);
    System.out.println("完成任务:任务Id:" + taskId);
    }

当我们完成这三个任务之后,执行SQL语句SELECT * FROM act_ru_execution,查询正在执行的执行对象表

正在执行的执行对象表2

可以看到,执行完收货任务后,我们还是可以在该表中查到该执行对象,只是其ACTID的值修改为了parallegateway2,这是因为我们所使用的是并行网关,先执行到并行网关出口的分支必须等待其它分支执行完毕再通过网关。

查询所有历史活动节点的记录

所有历史活动节点2

所有活动节点历史3

可以看到,现在总共有7个活动节点

最后,我们再完成最后一个任务-收款任务

1
2
3
4
5
6
7
8
9
10
/**
* 完成我的任务
*/
@Test
public void completeMyPersonalTask() {
String taskId = "4302";
// 完成任务的同时,设置流程变量message的值,来控制下一连线
processEngine.getTaskService().complete(taskId);
System.out.println("完成任务:任务Id:" + taskId);
}

这样,我们就完成了所有任务,再次查询所有活动节点的历史表

所有活动节点历史表3

所有活动节点历史表4

分析历史数据,可以看到,对于不同的执行对象,各有一条关于结束并行网关节点的历史记录,并且这两条记录比较特殊,其结束时间为空。

总结

  1. 一个流程中流程实例只有1个,执行对象有多个

  2. 并行网关的功能是基于进入和外出的顺序流的:

    • 分支(fork): 并行后的所有外出顺序流,为每个顺序流都创建一个并发分支
    • 汇聚(join): 所有到达并行网关的分支,都必须在此等待进入的分支, 直到所有进入顺序流的分支都到达以后, 流程就会通过汇聚网关
  3. 并行网关的进入和外出都是使用相同节点标识

  4. 如果同一个并行网关有多个进入和多个外出顺序流, 它就同时具有分支和汇聚功能。 这时,网关会先汇聚所有进入的顺序流,然后再切分成多个并行分支

  5. 并行网关不会解析条件。 即使顺序流中定义了条件,也会被忽略

  6. 并行网关不需要是“平衡的”(比如, 对应并行网关的进入和外出节点数目不一定相等)。如图中标示是合法的:

    不平衡的并行网关