flowable中終止流程(二)

終止流程代碼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()方法來運行
image.png
原來,這個地方拋出了異常。思考了好久,應該是併發致使的問題
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());
相關文章
相關標籤/搜索