乐趣区

关于工作流: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());
退出移动版