Apache ODE 流程運行過程解析

流程運行概述

流程運行需要引擎首先創建該流程的實例,當然實例的創建也是以接收用戶發送的調用消息起始的,然後根據該流程對象的定義按順序執行一個個活動,像賦值操作、等待操作等,當然最重要的還有調用外部服務的操作,同時還要負責接收發送回來的響應消息,根據響應消息的內容再執行後續的流程,最後流程終止後,銷燬該實例。


 SOAP消息的接收與傳遞 

在本系統中與外界的所有通信都由AXIS2來負責,通過其配置文件來指定不同的消息類型如何進行處理,比如可以給所有的進入消息配置一個處理器,該處理器的工作就是檢查該消息頭部,得知其是否有狀態,以及是否需要返回消息等。通過這樣的實現方式就可以對所有接受到的消息進行一次前處理,方便後續的操作。同樣,我們可以配置多個處理器,針對各種不同的操作都有相應的動作。這種通過配置文件就可以改變消息處理方式的做法非常簡單,這也說明AXIS2的設計擴展性非常好,可以很方便的與其他系統進行集成。

在上一節中說到流程編譯的最後一個步驟是將流程暴露爲Web服務,同時將該服務所有操作(Operation)都綁定一個消息接收器,這裏的消息接收器就可以認爲是引擎與AXIS2之間的橋樑,通過它來將AXIS2接收到的消息轉發到引擎內部。至於AXIS2是如何接收客戶端發送來的SOAP消息並不是本論文討論的重點,這個內容可以查看相關AXIS2的文檔。還應注意到的一點是,在引擎系統中所有的消息可以分爲兩種,一種是由外部客戶端發送過來的,而另一種是引擎內部自己作爲流程執行所需而傳遞的消息。對於外部消息可以採用AXIS2系統中的MessageConext類作爲消息表示,這就省去了消息格式轉化的步驟,但是這種類型消息只能使用在從外部接收到的內容,因爲它並不包含下一步的路由信息,不適用於引擎內部進行交互。在流程執行過程中,所有的消息應該採用內部封裝的格式,利用事件觸發的形式進行流程的執行。

在本系統中我們爲AXIS2配置了四個消息處理器,如圖,其中第一個消息進入處理器是對所有接收到的SOAP消息進行一個前期處理,來判斷此次調用是有狀態的,還是無狀態的,以及是否需要返回消息。首先判斷此次消息調用頭部信息中是否已經有了session標識和所要調用的endpoint信息,則將這些消息取出,以屬性的形式加入到AXIS2所接收的MessageContext中,同樣的,對於是否需要返回消息,也採用此辦法。這樣,在後續的操作中所有的此次調用信息都可以通過提取MessageContext中的屬性來獲得,比較方便,不用每次都從消息頭部中解析。第二個查找服務處理器,是根據請求地址URL中的服務名稱來查找的。在本系統中,對外暴露的服務所調用的地址模式是/processes/service-a這種形式,通過解析該字符串拿到service-a這樣的服務名稱,這樣便可以從服務註冊處(系統內部保持所有已提供服務的信息)拿到服務對象。第三個查找操作處理器是根據上一步中得到的服務對象和消息內容來確定調用的是哪個操作(operation)。這個操作的句柄可以很容易獲得,而這樣的操作在流程編譯階段就已經都綁定了固定的消息接收器,當AXIS2對該operation進行調用時,其所傳遞給該操作的入口參數(消息)也就是傳遞給了提前註冊好的消息接收器。在本系統中,所有由AXIS2轉發的消息都由同一個接收器進行攔截,在該接收器中判斷此次調用是否需要回應,然後再調用業務邏輯。至此,消息的接收和轉發就到此爲止了。


 流程實例的創建

 

根據接收到的消息可以創建一個新的流程實例,有了實例纔可以進行流程運行。第一步需要將接收到的消息轉化爲內部表示形式,轉化的過程一個最重要的步驟就是爲該消息加上唯一的UUID,這個標識將作爲後續多步操作的一個關聯子,通過它來將後續的操作串接起來,不至於多個消息到達後無法判斷應該發往哪個實例。還有,在消息轉化的過程中加入了該消息所對應的流程信息,因爲這時候的消息是啓動流程實例,其中並沒有帶有任何關聯子,系統無法判斷應該將其路由到哪裏,所以必須爲其指定流程信息從而可以創建流程實例。



 對於引擎來說需要同時處理各個不同客戶端發送的消息,在同時部署多個流程,多個流程實例在運行時會造成系統很大的負載,從而影響性能。在這裏我們採用事件機制,引擎處理流程執行的動作都將其封裝爲一個任務(Job),通過在一個線程中不斷檢查可執行任務隊列來執行一個個任務。在準備好消息之後,將消息通過任務數據類型封裝添加到事件隊列中,當任務從隊列中彈出時,就是執行該任務了。而任務的執行者需要根據任務的類型來判斷如何運行該任務,不同類型的任務交給不同的對象去處理。

 

流程實例的創建,就是要在內存中創建一個該流程運行的上下文環境,讓該流程所涉及的各個活動能夠在當前環境下運行,這個環境包括了初始消息、編譯後的流程定義、該流程的關聯子以及與該流程實例相關的變量等。這個包括了流程運行上下文環境的對象就是一個實例,新建該對象只是準備好了所有需要的信息,還需要啓動該實例。啓動實例實際的操作就是調用該流程的第一個活動,成功調用流程的第一個活動之後流程實例的創建就算完成了。

流程實例執行過程

 

實例的運行需要結合流程定義文件,而BPEL定義文件是可以分爲各種不同的標籤,代表了不同的語義,同時也就是對應了不同的操作。針對每個不同的標籤設計其運行方式,例如BPEL定義文件中最外層的PROCESS標籤就有自己特定的執行方式,它主要是來獲得其下層的子標籤,並啓動其子標籤的執行。每個不同的標籤都有自己的執行方法,但同時各個標籤之間都有着各種聯繫,像父標籤與子標籤的關係,並行關係等。

易於想到的流程執行實現方法就是,從流程定義的最外層標籤一層一層的進行調用,根據流程定義的條件判斷執行路徑,不斷的創建新的活動,並且調用它。這是最容易實現也最直觀的一種方式,但是它卻存在諸多問題。首先,這種方式佔用的內存空間很可能非常大。因爲父標籤所對應的對象佔用的空間,只有在其所有子標籤都執行完畢之後纔可以得到釋放,這樣它才能向它自己的父標籤報告執行結果,而如果流程嵌套的很深,其最外層的標籤將很長時間得不到釋放。而且一般實際生產過程中,一個實際的業務流程涉及到的操作節點很多,邏輯也十分複雜,對應到BPEL流程定義文件就是其所包含的標籤很多,其執行時間可能一兩天甚至幾個月都有可能,那麼這種情況下對於系統的性能影響將是非常之大的。其次,在流程運行過程中,如果需要對流程執行狀態進行持久化(用戶執行流程的掛起操作等情況),那麼就需要將當前執行線程調用堆棧中的所有對象彈出,同時對其執行狀態持久化存儲。這必然需要操作對象(也就是一個個標籤的實現類)的支持,這個需求將會大大增加每個標籤操作類的設計難度和複雜度。

因此我們在流程執行的設計中,將流程不同標籤的邏輯操作與標籤之間的通信操作相分離,標籤的實現類只負責其邏輯操作,調用子標籤和向父標籤告知其執行結果的工作不需要由當前標籤來完成,而重新建立一個專門的流程活動處理器,它建立兩個執行隊列(在這裏雖然叫做隊列,但並不保證其調用順序),一個保存需要執行的活動,我們稱其爲操作隊列;另一個保存各個活動執行結束之後的回調操作,叫做回調隊列。如果該活動在執行結束後需要告知其父活動,那麼在進入隊列的時候就需要提前將這標籤活動與回調操作進行綁定,也就是將這兩個調用進行關聯,那麼在執行隊列調用到該標籤操作時就可以正確的調用其事先指定的方法。在這裏標籤活動的隊列是一個主動查找的隊列,也就是在流程開始執行後,該隊列需要被不斷的檢查,一發現有可以執行的操作就調用,而另一個回調操作隊列則是被動查找的,因爲在每一個需要回調的活動進入隊列的時候就將一個與其對應的回調操作加入到了該隊列中,所以隨着該活動的被彈出執行,其回調方法也會被查找到進行執行。



 該模型就相當於一個生產者消費者模式,各個標籤定義的操作就是生產者,同時也是產品,隊列就相當於是緩衝池,流程活動處理器就是消費者。它三者之間的關係就是,操作的執行會將其子標籤的操作加入到隊列中,而流程活動處理器在不斷的檢查該緩衝池中是否有可用的產品,一旦發現有了可執行的操作就將其彈出執行,而同時這個彈出的操作有可能將新的操作加入到隊列中。

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