flowable中终止流程(二)

微信扫一扫,分享到朋友圈

flowable中终止流程(二)

终止流程代码

public void stopProcessInstanceById(String processInstanceId) {
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (processInstance != null) {
//1、获取终止节点
List<EndEvent> endNodes =findEndFlowElement(processInstance.getProcessDefinitionId());
String endId = endNodes.get(0).getId();
//2、执行终止
List<Execution> executions = runtimeService.createExecutionQuery().parentId(processInstanceId).list();
List<String> executionIds = new ArrayList<>();
executions.forEach(execution -> executionIds.add(execution.getId()));
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
log.info("终止processInstanceId:{}成功",processInstanceId);
}else {
log.info("不存在运行的流程实例processInstanceId:{},请确认!",processInstanceId);
}
}
public List findEndFlowElement(String processDefId) {
Process mainProcess = repositoryService.getBpmnModel(processDefId).getMainProcess();
Collection<FlowElement> list = mainProcess.getFlowElements();
if (CollectionUtils.isEmpty(list)) {
return Collections.EMPTY_LIST;
}
return list.stream().filter(f -> f instanceof EndEvent).collect(Collectors.toList());
}

坑1:网上搜索的一些帖子中,只调用

runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId)

并没有changeState,正确的使用姿势是

runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();

没有调用changeState方法是不会生效的

坑2:在serviceTask中调用

flowableInstanceService.stopProcessInstanceById(delegateExecution.getProcessInstanceId());

若该serviceTask中抛出Exception,会发生诡异的操作:

流程并没有顺利终止掉。

为什么?flowable中使用的默认的传播行为为Required(没有事务则新建一个事务),而serviceTask是在事务中执行的(可以了解flowable源码之命令模式,若servicetask中抛出异常,则相应的数据库操作会被全部回滚掉),结束流程这个方法会沿用这个事务,当该servicetask抛出错误后,会回滚掉准备提交的sqlsession

解决思路:

1.使用多线程,异步执行终止流程操作

executorService.submit(() -> flowableInstanceService.stopProcessInstanceById(delegateExecution.getProcessInstanceId()));

引发问题:史诗级巨坑,排查了很久,偶尔执行成功,偶尔执行失败,不抛错,也没有异常。

排查问题思路:flowableInstanceService.stopProcessInstanceById真的执行完毕了吗?为什么数据库的状态没有改变,这里是其他线程,没有再开启事务了,不存在之前的事务传播行为的问题。

通过打印日志的方式,我哭了

executorService.execute(() -> {
log.info("终止processInstanceId:{}任务开始",delegateExecution.getProcessInstanceId());
flowableInstanceService.stopProcessInstanceById(delegateExecution.getProcessInstanceId());
log.info("终止processInstanceId:{}任务结束",delegateExecution.getProcessInstanceId());
});

任务结束的日志竟然没有打印,我怀疑可能抛出了异常,为了验证问题所在,线程增加日志打印处理

@Bean
public ExecutorService executorService() {
return Executors.newSingleThreadExecutor(new HandleThreadFactory());
}
class HandleThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
System.out.println("create thread t");
Thread t = new Thread(r);
System.out.println("set uncaughtException for t");
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("caught exception:" + e);
}
});
return t;
}
}

无果??异常没有被捕获到,原来,使用executorService.execute()才会被捕捉到,于是更换成executorService.execute()方法来运行

原来,这个地方抛出了异常。思考了很久,应该是并发导致的问题

serviceTask的线程为A线程,结束流程的线程为B线程

A线程当前任务节点为servicetask1,

B线程读取当前任务节点为servicetask1,并企图移动到end节点

A线程抛错或者serviceTask1执行完毕,任务节点多会变动为定时器(重试机制)或者serviceTask2(正常流转)

B线程执行操作,移动到end节点,伪代码sql为update to end where node is serviceTask1,但是注意注意,A线程已经commit了具体的最新的当前节点的值,A线程拿到的还是老数据,故操作失败,updateCount为0,抛出异常

拓展:flowable为了并发性能,使用的是伪并发,即乐观锁机制,乐观的认为所有的数据都是没有竞争的,不加重锁。失败就会抛出此异常

最终解决办法:

不抛异常,直接同步终止流程

flowableInstanceService.stopProcessInstanceById(delegateExecution.getProcessInstanceId());

微信扫一扫,分享到朋友圈

flowable中终止流程(二)

外媒:iPhone12 5G体验可能并没有人们想象中那么好

上一篇

十分钟带你理解C语言中的链表

下一篇

你也可能喜欢

flowable中终止流程(二)

长按储存图像,分享给朋友