共计 3429 个字符,预计需要花费 9 分钟才能阅读完成。
终止流程代码
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());