8.6 子流程和調用節點

Sub-Processes and Call Activities 子流程和調用節點

Sub-Process

Description 描述

子流程(Sub-process)是一個包含其他節點,網關,事件等等的節點。 它自己就是一個流程,同時是更大流程的一部分。 子流程是完全定義在父流程裏的 (這就是爲什麼叫做內嵌子流程)。

子流程有兩種主要場景:

  • 子流程可以使用繼承式建模。 很多建模工具的子流程可以摺疊, 把子流程的內部細節隱藏,顯示一個高級別的端對端的業務流程總覽。
  • 子流程會創建一個新的事件作用域。 子流程運行過程中拋出的事件,可以被子流程邊緣定義的 邊界事件捕獲, 這樣就可以創建一個僅限於這個子流程的事件作用範圍。

使用子流程要考慮如下限制:

  • 子流程只能包含一個空開始事件,不能使用其他類型的開始事件。子路程必須 至少有一個結束節點。注意,BPMN 2.0 規範允許忽略子流程的 開始和結束節點,但是當前 activiti 的實現並不支持。
  • 順序流不能跨越子流程的邊界。

Graphical notation 圖形標記

子流程顯示爲標準的節點,圓角矩形。 這時子流程是摺疊的,只顯示名稱和一個加號標記,展示了高級別的流程總覽:

這時子流程是展開的,子流程的步驟都顯示在子流程邊界內:

使用子流程的主要原因,是定義對應事件的作用域。下面流程模型演示了這個功能:調查軟件/調查引薦任務需要同步執行,兩個任務需要在同時完成,在二線支持解決之前。 這裏,定時器的作用域(比如,節點需要及時完成)是由子流程限制的。

XML representation 內容

子流程定義爲 subprocess 元素。 所有節點,網關,事件,等等。它是子流程的一部分,需要放在這個元素裏。

<subProcess id="subProcess">

  <startEvent id="subProcessStart" />

  ... other Sub-Process elements ...

  <endEvent id="subProcessEnd" />

 </subProcess>     

Event Sub-Process 事件子流程

Description 描述

事件子流程是 BPMN 2.0 中的新元素。事件子流程是由事件觸發的子流程。 事件子流程可以添加到流程級別或任意子流程級別。用於觸發事件子流程的事件是使用開始事件配置的。爲此,事件子流程是不支持空開始事件的。 事件子流程可以被消息事件,錯誤事件,信號事件,定時器事件,或補償事件觸發。開始事件的訂閱在包含事件子流程的作用域(流程實例
或子流程)創建時就會創建。 當作用域銷燬也會刪除訂閱。

事件子流程可以是中斷的或非中斷的。一箇中斷的子流程會取消當前作用域內的所有流程。非中斷事件子流程會創建那一個新的同步分支。中斷事件子流程只會被每個激活狀態的宿主觸發一次, 非中斷事件子流程可以觸發多次。子流程是否是終端的,配置使用事件子流程的開始事件配置。

事件子流程不能有任何進入和外出流程。當事件觸發一個事件子流程時,輸入順序流是沒有意義的。 當事件子流程結束時,無論當前作用域已經結束了(中斷事件子流程的情況),或爲非中斷子流程生成同步分支會結束。

當前的限制:
* activiti只支持中斷事件子流程。
* activiti只支持使用錯誤開始事件或消息開始事件的事件子流程。

Graphical notation 圖形標記

事件子流程可以顯示爲邊框爲虛線的內嵌子流程。

XML representation 內容

事件子流程的 XML 內容與內嵌子流程是一樣的。 另外,要把triggeredByEvent 屬性設置爲 true:

<subProcess id="eventSubProcess" triggeredByEvent="true">
        ...
</subProcess>

Example 實例

下面是一個使用錯誤開始事件觸發的事件子流程的實例。事件子流程是放在“流程級別”的,意思是,作用於流程實例:

事件子流程的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>

如上面所述,事件子流程也可以添加成內嵌子流程。如果添加爲內嵌子流程,它其實是邊界事件的一種替代方案。 考慮下面兩個流程圖。兩種情況內嵌子流程會拋出一個錯誤事件。兩種情況錯誤都會被捕獲並使用一個用戶任務處理。

相對於:

兩種場景都會執行相同的任務。然而,兩種建模的方式是不同的:

  • 內嵌子流程是使用與執行作用域宿主相同的流程執行的。意思是內嵌子流程可以訪問它作用域內的內部變量。 當使用邊界事件時,執行內嵌子流程的流程會刪除,並生成一個流程根據邊界事件的順序流繼續執行。 這意味着內嵌子流程創建的變量不再起作用了。
  • 當使用事件子流程時,事件是完全由它添加的子流程處理的。 當使用邊界事件時,事件由父流程處理。

這兩個不同點可以幫助我們決定是使用邊界事件還是內嵌事件子流程來解決特定的流程建模/實現問題。

Transaction subprocess 事務子流程

[試驗]

Description 描述

事務子流程是內嵌子流程,可以用來把多個流程放到一個事務裏。 事務是一個邏輯單元,可以把一些單獨的節點放在一起,這樣它們就可以一起成功或一起失敗。事務可能的結果: 事務可以有三種可能的結果:

  • 事務成功,如果沒有取消也沒有因爲問題終結。如果事務子流程是成功的, 就會使用外出順序流繼續執行。 如果流程後來拋出了一個補償事件,成功的事務可能被補償。
    注意:和普通內嵌子流程一樣,事務可能在成功後, 使用中間補償事件進行補償。
  • 事務取消,如果流程到達取消結束事件。這時,所有流程都會終結和刪除。觸發補償的一個單獨的流程,會通過取消邊界事件繼續執行。 在補償完成之後,事務子流程會使用取消邊界事務的外出順序流向下執行。
  • 事務被問題結束,如果跑出了一個錯誤事件, 而且沒有在事務子流程中捕獲。(如果錯誤被事務子流程的邊界事件處理了,也會這樣應用。) 這時,不會執行補償。

下面的圖形演示了三種不同的結果:

與A CID 事務的關係:一定不要把 bpmn 事務子流程與技術(ACID)事務相混淆。 bpmn事務子流程不是技術事務領域的東西。要理解 activiti 中的事務管理,請參考 [併發與事務](Transactions and Concurrency 事務與併發.md)。 bpmn 事務和技術事務有以下不同點:

  • ACID事務一般是短期的,bpmn事務可能持續幾小時,幾天,甚至幾個月才能完成。(考慮事務中包含的節點可能有用戶任務,一般人員響應的時間比應用時間要長。或者,在其他情況下,bpmn 事務可能要等待發生一些事務事件,就像要根據某種次序執行。) 這種操作通常要相比更新數據庫的一條數據,或把一條信息保存到事務性隊列中,消耗更長的時間來完成。
  • 因爲不能在整個業務節點的過程中保持一個技術性的事務,所以 bpmn 事務一般要跨越多個 ACID 事務。
  • 因爲 bpmn 事務會跨越多個 ACID 事務,所以會喪失 ACID 的特性。比如,考慮上述例子。 假設“約定旅店”和“刷信用卡”操作在單獨的ACID事務中執行。 也假設“預定旅店”節點已經成功了。現在我們處於一箇中間不穩定狀態,因爲我們預定了酒店,但是還沒有刷信用卡。 現在,在一個ACID事務中,我們要依次執行不同的操作,也會有一箇中間不穩定狀態。 不同的是,這個中間狀態對事務的外部是可見的。 比如,如果通過外部預定服務進行了預定,其他使用相同預定服務的部分就可以看到旅店被預定了。 這意味着實現業務事務時,我們完全失去了隔離屬性 (注:我們也經常放棄隔離性,來爲ACID事務獲得更高的併發,但是我們可以完全控制,中間不穩定狀態也只持續很短的時間)。

  • bpmn 業務事務也不能使用通常的方式回滾。因爲它跨越了多個事務,bpmn 事務取消時一些 ACID 事務可能已經提交了。 這時,它們不能被回滾了。

因爲 bpmn 事務實際上運行時間很長,缺乏隔離性和回滾機制都需要被區別對待。 實際上,這裏也沒有更好的辦法在特定領域處理這些問題:

  • 使用補償執行回滾。如果事務範圍拋出了取消事件,會影響已經執行成功的節點, 並使用補償處理器執行補償。
  • 隔離性的缺乏通常使用特定領域的解決方法來解決。比如,上面的例子中, 一個旅店房間可能會展示給第二個客戶,在我們確認第一個客戶付費之前。 雖然這可能與業務預期不符,預定服務可能選擇允許一些過度的預約。
  • 另外,因爲事務會因爲風險而中斷,預定服務必須處理這種情況,已經預定了旅店,但是一直沒有付款的情況。 (因爲事務被中斷了)。這時預定服務需要選擇一個策略,在旅店房間預定超過最大允許時間後, 如果還沒有付款,預定就會取消。

綜上所述:ACID 處理的是通常問題(回滾,隔離級別和啓發式結果), 在實現業務事務時,我們需要找到特定領域的解決方案來處理這些問題。

目前的限制

  • bpmn規範要求流程引擎能根據底層事務的協議處理事件,比如如果底層協議觸發了取消事件,事務就會取消。 作爲內嵌引擎,activiti目前不支持這項功能。(對此造成的後果,可以參考下面的一致性討論)。

ACID事務頂層的一致性和優化併發: bpmn 事務保證一致性,要麼所有節點都成功,或者一些節點成功,對其他成功的節點進行補償。 無論哪種方式,都會有一致性的結果。不過要討論一些 activiti 內部的情況,bpmn 事務的一致性模型是疊加在流程的一致性模型之上的。activiti 執行流程是事務性的。併發使用了樂觀鎖。在 activiti 中,bpmn 錯誤,取消和補償事件都建立在同樣的 acid 事務與樂觀鎖之上。 比如,取消結束事件只能觸發它實際到達的補償。如果之前服務任務拋出了未聲明的異常。或者, 補償處理器的效果無法提交,如果底層的 acid 事務的參與者把事務設置成必須回滾。 或者當兩個併發流程到達了取消結束事件,可
能會觸發兩次補償,並因爲樂觀鎖異常失敗。 所有這些都說明 activiti 中實現 bpmn 事務時,相同的規則也作用域普通的流程和子流程。 所以爲了保證一致性,重要的是使用一種方式考慮實現樂觀事務性的執行模型。

Graphical notation 圖形標記

事務子流程顯示爲內嵌子流程,使用雙線邊框。

XML representation 內容

事務子流程使用 transaction 標籤:

<transaction id="myTransaction" >
        ...
</transaction>

Example 實例

下面是事務子流程的實例:

Call activity (subprocess) 調用活動(子流程)

Description 描述

BPMN 2.0 區分了普通子流程, 也叫做內嵌子流程,和調用節點,看起來很相似。 從概念上講,當流程抵達時,兩者都會調用子流程。

不同點是調用節點引用流程定義外部的一個流程,子流程 會內嵌到原始的流程定義中。使用調用節點的主要場景是需要重用流程定義, 這個流程定義需要被很多其他流程定義調用的時候。

當流程執行到調用節點,會創建一個新分支,它是到達調用節點的流程的分支。 這個分支會用來執行子流程,默認創建並行子流程,就像一個普通的流程。 上級流程會等待子流程完成,然後纔會繼續向下執行

Graphical notation 圖形標記

調用節點顯示與子流程相同, 不過是粗邊框(無論是摺疊和展開的)。 根據不同的建模工具,調用節點也可以展開,但是顯示爲摺疊的子流程。

XML representation 內容

有種調用節點是經常性的節點,需要 calledElement 引用被 key 定義的流程。在實踐中,這意味着流程的 ID 用於 calledElement。

<callActivity id="callCheckCreditProcess" name="Check credit" calledElement="checkCreditProcess" />

注意,子流程的流程定義是在執行階段解析的。 就是說子流程可以與調用的流程分開部署,如果需要的話。

Passing variables 傳遞變量

可以把流程變量傳遞給子流程,反之亦然。數據會複製給子流程,當它啓動的時候, 並在它結束的時候複製回主流程。

<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

Example 實例

下面的流程圖演示了簡單訂單處理。先判斷客戶端信用,這可能與很多其他流程相同。 檢查信用階段這裏設計成調用節點。

流程看起來像這樣的:

<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" />

子流程看起來像這樣的:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章