子流程
子流程
描述
- 子流程(Sub-process)是一個包含其他節點,網關,事件等等的節點
- 本身就是一個流程,同時是更大流程的一部分.子流程是完全定義在父流程里的,所以叫做內嵌子流程
- 子流程的兩種主要場景:
- 子流程可以使用繼承式建模: 很多建模工具的子流程可以折疊,把子流程的內部細節隱藏,顯示一個高級別的端對端的業務流程總覽
- 子流程會創建一個新的事件作用域: 子流程運行過程中拋出的事件,可以被子流程邊緣定義的邊界事件捕獲,就可以創建一個僅限於這個子流程的事件作用范圍
- 使用子流程的限制:
- 子流程只能包含一個空開始事件, 不能使用其他類型的開始事件,子路程必須至少有一個結束節點
- 順序流不能跨越子流程的邊界
圖形標記
- 子流程顯示為標准的節點(圓角矩形),下面子流程是折疊的,只顯示名稱和一個加號標記,展示了高級別的流程總覽:
- 下面子流程是展開的,子流程的步驟都顯示在子流程邊界內:
- 使用子流程主要是為了定義對應事件的作用域
- 示例:
- 調查軟件/調查引薦任務需要同步執行,兩個任務需要在同時完成,在二線支持解決之前.這里,定時器的作用域(比如節點需要及時完成)是由子流程限制的:
XML內容
- 子流程定義為subprocess元素.所有節點,網關,事件,等等.是子流程的一部分,都需要放在這個元素里
<subProcess id="subProcess">
<startEvent id="subProcessStart" />
... other Sub-Process elements ...
<endEvent id="subProcessEnd" />
</subProcess>
事件子流程
描述
- 事件子流程是由事件觸發的子流程.是BPMN 2.0中的新元素
- 事件子流程可以添加到流程級別或任意子流程級別
- 用於觸發事件子流程的事件是使用開始事件配置的,所以事件子流程是不支持空開始事件的
- 事件子流程可以被消息事件,錯誤事件,信號事件,定時器事件,或補償事件觸發.開始事件的訂閱在包含事件子流程的作用域(流程實例或子流程)創建時就會創建.當作用域銷毀也會刪除訂閱。
- 事件子流程可以是中斷的或非中斷的
- 一個中斷的子流程會取消當前作用域內的所有流程
- 非中斷事件子流程會創建一個新的同步分支
- 中斷事件子流程只會被每個激活狀態的宿主觸發一次
- 非中斷事件子流程可以觸發多次
- 子流程是否是中斷的,使用事件子流程的開始事件配置
- 事件子流程不能有任何進入和外出流程
- 當事件觸發一個事件子流程時,輸入順序流是沒有意義的
- 當事件子流程結束時,無論當前作用域已經結束(中斷事件子流程的情況或為非中斷,子流程生成同步分支會結束
- 事件子流程的限制:
- Activiti只支持中斷事件子流程
- Activiti只支持使用錯誤開始事件或消息開始事件的事件子流程
圖像標記
- 事件子流程可以顯示為邊框為虛線的內嵌子流程
XML內容
- 事件子流程的XML內容與內嵌子流程一樣,但是要把triggeredByEvent屬性設置為true
<subProcess id="eventSubProcess" triggeredByEvent="true">
...
</subProcess>
實例
- 使用錯誤開始事件觸發的事件子流程的實例,事件子流程是放在流程級別的,作用於流程實例
事件子流程的XML:
<subProcess id="eventSubProcess" triggeredByEvent="true">
<startEvent id="catchError">
<errorEventDefinition errorRef="error" />
</startEvent>
<sequenceFlow id="flow2" sourceRef="catchError" targetRef="taskAfterErrorCatch" />
<userTask id="taskAfterErrorCatch" name="Provide additional data" />
</subProcess>
事件子流程也可以添加成內嵌子流程.如果添加為內嵌子流程,其實是邊界事件的一種替代方案
- 示例:
- 下面兩個流程圖,兩種情況內嵌子流程會拋出一個錯誤事件,兩種情況錯誤都會被捕獲並使用一個用戶任務處理
相對於
- 兩種場景都會執行相同的任務,但是兩種建模的方式是不同的:
- 內嵌子流程是使用與執行作用域宿主相同的流程執行的:
- 意思是內嵌子流程可以訪問它作用域內的內部變量
- 當使用邊界事件時,執行內嵌子流程的流程會刪除, 並生成一個流程根據邊界事件的順序流繼續執行,這意味着內嵌子流程創建的變量不再起作用
- 當使用事件子流程時,事件是完全由它添加的子流程處理的.
- 當使用邊界事件時,事件由父流程處理
- 內嵌子流程是使用與執行作用域宿主相同的流程執行的:
- 下面兩個流程圖,兩種情況內嵌子流程會拋出一個錯誤事件,兩種情況錯誤都會被捕獲並使用一個用戶任務處理
- 這兩個不同點可以幫助決定是使用邊界事件(內嵌子流程)還是內嵌事件子流程(事件子流程) 來解決特定的流程建模或者實現問題
事務子流程
描述
- 事務子流程是內嵌子流程, 可以用來把多個流程放到一個事務里
- 事務是一個邏輯單元, 可以把一些單獨的節點放在一起, 這樣它們就可以一起成功或一起失敗
- 事務的可能結果有三種:
- 事務成功,沒有取消也沒有因為問題終結
- 如果事務子流程是成功的,就會使用外出順序流繼續執行
- 如果流程后來拋出了一個補償事件,成功的事務可能被補償
- 和普通內嵌子流程一樣,事務可能在成功后,使用中間補償事件進行補償
- 事務取消,流程到達取消結束事件
- 所有流程都會終結和刪除,觸發補償的一個單獨的流程,會通過取消邊界事件繼續執行
- 在補償完成之后,事務子流程會使用取消邊界事務的外出順序流向下執行
- 事務被問題結束,拋出一個錯誤事件而且沒有在事務子流程中捕獲(如果錯誤被事務子流程的邊界事件處理了,也會這樣應用)
- 不會執行補償
- 事務成功,沒有取消也沒有因為問題終結
- 事務三種不同的結果:
- BPMN事務與ACID(技術)事務的關系: BPMN事務子流程與技術(ACID)事務不能互相混淆,BPMN事務子流程不是技術(ACID)事務領域的
- BPMN事務和技術事務有以下不同點:
- ACID事務一般是短期的.BPMN事務可能持續幾小時,幾天,甚至幾個月才能完成:
- 考慮事務中包含的節點可能有用戶任務,一般人員響應的時間比應用時間要長
- 在其他情況下,bpmn事務可能要等待發生一些事務事件,例如要根據某種次序執行
- 這種操作通常要相比更新數據庫的一條數據,或把一條信息保存到事務性隊列中,消耗更長的時間來完成
- BPMN事務一般要跨越多個ACID事務,因為不能在整個業務節點的過程中保持一個技術性的事務
- BPMN事務會跨越多個ACID事務,所以會喪失ACID的特性:
- 比如,在上述例子中,假設預訂旅店和刷信用卡操作在單獨的ACID事務中執行,假設預定旅店節點已經成功了
- 現在處於一個中間不穩定狀態,因為我們預定了酒店,但是還沒有刷信用卡
- 在一個ACID事務中,要依次執行不同的操作,也會有一個中間不穩定狀態
- 不同的是,這個中間狀態對事務的外部是可見的.比如,如果通過外部預定服務進行了預定,其他使用相同預定服務的部分就可以看到旅店被預定了.這意味着實現業務事務時,我們完全失去了隔離屬性(放棄隔離性,可以為ACID事務獲得更高的並發,是可以完全控制,中間不穩定狀態也只持續很短的時間)
- BPMN業務事務也不能使用通常的方式回滾:
- BPMN事務跨越了多個事務,BPMN事務取消時一些ACID事務可能已經提交了.這時不能被回滾
- ACID事務一般是短期的.BPMN事務可能持續幾小時,幾天,甚至幾個月才能完成:
- BPMN事務運行時間很長,缺乏隔離性和回滾機制都需要被區別對待:
- 使用補償執行回滾:
- 如果事務范圍拋出了取消事件,會影響已經執行成功的節點,並使用補償處理器執行補償
- 隔離性的缺乏通常使用特定領域的解決方法來解決:
- 在上面的例子中,一個旅店房間可能會展示給第二個客戶,在我們確認第一個客戶付費之前.雖然這可能與業務預期不符,預定服務可能選擇允許一些過度的預約
- 事務會因為風險而中斷,服務必須處理這種情況:
- 已經預定了旅店,但是一直沒有付款的情況(因為事務被中斷了),這時預定服務需要選擇一個策略,在旅店房間預定超過最大允許時間后,如果還沒有付款,預定就會取消
- 使用補償執行回滾:
- 綜上所述,ACID處理的是通常問題:回滾,隔離級別和啟發式結果,在實現業務事務時,需要找到特定領域的解決方案來處理這些問題
- BPMN事務目前的限制:
- BPMN規范要求流程引擎能根據底層事務的協議處理事件:
- 比如如果底層協議觸發了取消事件,事務就會取消
- BPMN規范要求流程引擎能根據底層事務的協議處理事件:
- ACID事務頂層的一致性和優化並發:
- BPMN事務保證一致性:
- 要么所有節點都成功
- 一些節點成功,對其他成功的節點進行補償
- 無論哪種方式,都會有一致性的結果
- 要討論一些activiti內部的情況BPMN事務的一致性模型是疊加在流程的一致性模型之上的
- Activiti執行流程是事務性的,並發使用了樂觀鎖.在Activiti中,BPMN錯誤,取消和補償事件都建立在同樣的ACID事務與樂觀鎖之上:
- 取消結束事件只能觸發它實際到達的補償
- 如果之前服務任務拋出了未聲明的異常
- 補償處理器的效果無法提交,如果底層的acid事務的參與者把事務設置成必須回滾.
- 當兩個並發流程到達了取消結束事件
- 可能會觸發兩次補償,並因為樂觀鎖異常失敗
- 說明Activiti中實現BPMN事務時,相同的規則也作用域普通的流程和子流程
- 為了保證一致性,重要的是使用一種方式考慮實現樂觀事務性的執行模型
- BPMN事務保證一致性:
圖形標記
- 事務子流程顯示為內嵌子流程, 使用雙線邊框
XML內容
- 事務子流程使用transaction標簽
<transaction id="myTransaction" >
...
</transaction>
實例
調用活動(子流程)
描述
- BPMN 2.0區分了普通子流程(內嵌子流程)和調用節點:
- 相同點: 當流程抵達節點時兩者都會調用子流程
- 不同點:
- 調用節點引用流程定義外部的一個流程
- 子流程會內嵌到原始的流程定義中
- 使用調用節點的主要場景: 需要重用流程定義,這個流程定義需要被很多其他流程定義調用
- 當流程執行到調用節點,會創建一個新分支,是到達調用節點的流程的分支
- 這個分支會用來執行子流程,默認創建並行子流程,就像一個普通的流程
- 上級流程會等待子流程完成,然后才會繼續向下執行
圖形標記
- 調用節點顯示與子流程相同,但是粗邊框(無論是折疊和展開的). 根據不同的建模工具,調用節點也可以展開,但是顯示為折疊的子流程
XML內容
<callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" />
- 子流程的流程定義是在執行階段解析的,就是說子流程可以與調用的流程分開部署
傳遞變量
- 可以把流程變量傳遞給子流程,反之亦然: 當它啟動的時候, 數據會復制給子流程,並在它結束的時候復制回主流程
<callActivity id="callSubProcess" calledElement="checkCreditProcess" >
<extensionElements>
<activiti:in source="someVariableInMainProcess" target="nameOfVariableInSubProcess" />
<activiti:out source="someVariableInSubProcss" target="nameOfVariableInMainProcess" />
</extensionElements>
</callActivity>
這里使用Activiti擴展來簡化BPMN標准元素調用dataInputAssociation和 dataOutputAssociation, 只在使用BPMN 2.0標准方式聲明流程變量有效
- 也可以使用表達式:
<callActivity id="callSubProcess" calledElement="checkCreditProcess" >
<extensionElements>
<activiti:in sourceExpression="${x+5}"" target="y" />
<activiti:out source="${y+5}" target="z" />
</extensionElements>
</callActivity>
z = y + 5 = x + 5 + 5
實例
- 訂單處理流程圖:先判斷客戶端信用,檢查信用階段設計成調用節點
- 流程XML:
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="receiveOrder" />
<manualTask id="receiveOrder" name="Receive Order" />
<sequenceFlow id="flow2" sourceRef="receiveOrder" targetRef="callCheckCreditProcess" />
<callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" />
<sequenceFlow id="flow3" sourceRef="callCheckCreditProcess" targetRef="prepareAndShipTask" />
<userTask id="prepareAndShipTask" name="Prepare and Ship" />
<sequenceFlow id="flow4" sourceRef="prepareAndShipTask" targetRef="end" />
<endEvent id="end" />
- 子流程: