终止流程代码node
public void stopProcessInstanceById(String processInstanceId) { ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); if (processInstance != null) { //一、获取终止节点 List<EndEvent> endNodes =findEndFlowElement(processInstance.getProcessDefinitionId()); String endId = endNodes.get(0).getId(); //二、执行终止 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:网上搜索的一些帖子中,只调用sql
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId)
并无changeState,正确的使用姿式是数据库
runtimeService.createChangeActivityStateBuilder().moveExecutionsToSingleActivityId(executionIds, endId).changeState();
没有调用changeState方法是不会生效的
坑2:在serviceTask中调用session
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为了并发性能,使用的是伪并发,即乐观锁机制,乐观的认为全部的数据都是没有竞争的,不加剧锁。失败就会抛出此异常
最终解决办法:
不抛异常,直接同步终止流程ide
flowableInstanceService.stopProcessInstanceById(delegateExecution.getProcessInstanceId());