任务的多实例和串并行 - AndiHappy/customcamunda GitHub Wiki
- 多实例是作为了一个单独的扩展元素来进行说明的。意思也就是说它可以和其他的BPMN组件结合起来使用,能够结合的BPMN组件有:
Service Task
Send Task
User Task
Business Rule Task
Script Task
Receive Task
Manual Task
(Embedded) Sub-Process
Call Activity
Transaction Subprocess
按照最常见的User Task进行说明。
-
Camunda引擎中支持多实例的行为主要是:MultiInstanceActivityBehavior,以及集成这个类的:SequentialMultiInstanceActivityBehavior和ParallelMultiInstanceActivityBehavior,这两个类的响应是通过多实例扩展元素的属性isSequential="true" 来决定的。
-
具体的响应的过程:
MultiInstanceActivityBehavior
public static final String NUMBER_OF_INSTANCES = "nrOfInstances";
public static final String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances";
public static final String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances";
这个是MultiInstanceActivityBehavior定义的三个常量,在流程定义的文件中可以直接的使用。实例个数,未完成实例个数以及已完成实例个数。
SequentialMultiInstanceActivityBehavior
public void complete(ActivityExecution scopeExecution) {
int loopCounter = getLoopVariable(scopeExecution, LOOP_COUNTER) + 1;
int nrOfInstances = getLoopVariable(scopeExecution, NUMBER_OF_INSTANCES);
int nrOfCompletedInstances = getLoopVariable(scopeExecution, NUMBER_OF_COMPLETED_INSTANCES) + 1;
setLoopVariable(scopeExecution, NUMBER_OF_COMPLETED_INSTANCES, nrOfCompletedInstances);
//完成的条件
if (loopCounter == nrOfInstances || completionConditionSatisfied(scopeExecution)) {
leave(scopeExecution);
}
else {
PvmActivity innerActivity = getInnerActivity(scopeExecution.getActivity());
performInstance(scopeExecution, innerActivity, loopCounter);
}
}
从SequentialMultiInstanceActivityBehavior 上面可以看出多实例完成的条件判断,如果全部的实例执行完毕或者定义的完成条件执行完毕,这个实例就算是执行完毕了。
下面是具体的配置可生成的任务了,具体的流程的样子是:
userTask中的配置
//顺序的生成三个代办的任务:
<bpmn:multiInstanceLoopCharacteristics isSequential="true" >
<bpmn:loopCardinality>3</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>
isSequential为true的情况下,会首先生成一个task,这个task完成以后,再次生成第二个针对同一个activitiId:Task_01 的task,完成以后,再同样的生成第三个。
16:10:58.704 INFO CamundaBaseTest [170] : task:id:null,activitiId:EndEvent_0a13dh0,activityName:null,assignee:null,endTime:Sat Apr 21 16:10:59 CST 2018
16:10:58.704 INFO CamundaBaseTest [170] : task:id:null,activitiId:StartEvent_1,activityName:null,assignee:null,endTime:Sat Apr 21 16:10:30 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:null,activitiId:Task_01#multiInstanceBody,activityName:null,assignee:null,endTime:Sat Apr 21 16:10:59 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:5513,activitiId:Task_01,activityName:task1,assignee:role,endTime:Sat Apr 21 16:10:40 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:5602,activitiId:Task_01,activityName:task1,assignee:role,endTime:Sat Apr 21 16:10:49 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:5702,activitiId:Task_01,activityName:task1,assignee:role,endTime:Sat Apr 21 16:10:59 CST 2018
16:10:58.705 INFO CamundaBaseTest [170] : task:id:null,activitiId:Task_02,activityName:task2,assignee:null,endTime:Sat Apr 21 16:10:59 CST 2018
//顺序的生成三个代办的任务:
<bpmn:multiInstanceLoopCharacteristics isSequential=“false" >
<bpmn:loopCardinality>3</bpmn:loopCardinality>
</bpmn:multiInstanceLoopCharacteristics>
isSequential为false的情况下,会同时生成单个task,这三个task的审批顺序就没有前后的关系,知道三个人同时审批过了,才会进入下一级的审批
16:12:58.074 INFO CamundaBaseTest [170] : ------------------------------------------------------
16:13:00.947 INFO CamundaBaseTest [172] : task:id:null,activitiId:StartEvent_1,activityName:null,assignee:null,endTime:Sat Apr 21 16:12:43 CST 2018
16:13:00.947 INFO CamundaBaseTest [172] : task:id:null,activitiId:Task_01#multiInstanceBody,activityName:null,assignee:null,endTime:null
16:13:00.947 INFO CamundaBaseTest [172] : task:id:5918,activitiId:Task_01,activityName:task1,assignee:role,endTime:null
16:13:00.948 INFO CamundaBaseTest [172] : task:id:5920,activitiId:Task_01,activityName:task1,assignee:role,endTime:null
16:13:00.948 INFO CamundaBaseTest [172] : task:id:5922,activitiId:Task_01,activityName:task1,assignee:role,endTime:null
下面的这个是并发的配置以及生成的task的样式:
<bpmn:multiInstanceLoopCharacteristics isSequential="false">
<bpmn:loopDataInputRef>assigneeList</bpmn:loopDataInputRef>
<bpmn:inputDataItem name="assignee" />
</bpmn:multiInstanceLoopCharacteristics>
并发实例的响应行为对应的逻辑:
public class ParallelMultiInstanceActivityBehavior extends MultiInstanceActivityBehavior implements MigrationObserverBehavior {
@Override
protected void createInstances(ActivityExecution execution, int nrOfInstances) throws Exception {
PvmActivity innerActivity = getInnerActivity(execution.getActivity());
// initialize the scope and create the desired number of child executions
prepareScopeExecution(execution, nrOfInstances);
List<ActivityExecution> concurrentExecutions = new ArrayList<ActivityExecution>();
for (int i = 0; i < nrOfInstances; i++) {
concurrentExecutions.add(createConcurrentExecution(execution));
}
// start the concurrent child executions
// start executions in reverse order (order will be reversed again in command context with the effect that they are
// actually be started in correct order :) )
for (int i = (nrOfInstances - 1); i >= 0; i--) {
ActivityExecution activityExecution = concurrentExecutions.get(i);
//这个i保证在生成对应任务的时候,是轮流生成的,一个一个的带进去的
performInstance(activityExecution, innerActivity, i);
}
}
//这里面的collectionElementVariable,是通过:inputDataItem 设置的,一个里面只能设置一个字段值 生成的任务形式是:
16:55:09.351 INFO CamundaBaseTest [187] : task:id:null,activitiId:StartEvent_1,activityName:null,assignee:null,endTime:Sat Apr 21 16:39:12 CST 2018
16:55:09.352 INFO CamundaBaseTest [187] : task:id:null,activitiId:Task_01#multiInstanceBody,activityName:null,assignee:null,endTime:null
16:55:09.352 INFO CamundaBaseTest [187] : task:id:6424,activitiId:Task_01,activityName:My Task,assignee:100,endTime:null
16:55:09.353 INFO CamundaBaseTest [187] : task:id:6426,activitiId:Task_01,activityName:My Task,assignee:200,endTime:null
16:55:09.353 INFO CamundaBaseTest [187] : task:id:6428,activitiId:Task_01,activityName:My Task,assignee:300,endTime:null
下面的这种是对上面设置的一种优化:
<userTask id="miTasks" name="My Task" camunda:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
camunda:collection="${myService.resolveUsersForTask()}" camunda:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>
只不过使用的是myService.resolveUsersForTask() 服务调用的方式,另外配置方式,简单了一点。
在下面的这种模式,就是针对完成条件,进行的限制,这种条件就是完成率达到0.6,也就是百分之60的时候,就能够直接运行到下一个审批节点
适合做投票的流程操作
<userTask id="miTasks" name="My Task" camunda:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false"
camunda:collection="assigneeList" camunda:elementVariable="assignee" >
<completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
