終止流程代碼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());