一、什么是工作流
工作流,是把業務之間的各個步驟以及規則進行抽象和概括性的描述。使用特定的語言為業務流程建模,讓其運行在計算機上,並讓計算機進行計算和推動。工作流是復雜版本的狀態機。
上圖為工作流退化為基礎狀態機的例子,小明的狀態非常簡單,站立->走路->跑步->走路->站立,無限循環,如果讓我們實現小明的狀態切換,那么我們只需要用一個字段來記錄小明當前的狀態就好了。
而對於復雜的狀態或者狀態維度增加且狀態流轉的條件極為復雜,可能單純用字段記錄狀態的實現方式就會不那么理想。
如上圖所示,現在交給小明的選擇就多了起來,當小明獲得金錢的時候,他會根據金錢數額的大小來判斷接下來該如何行動,如果數額小於等於3000,那么他決定買一個新手機就夠了,如果數額小於等於30萬,那么小明就決定去學習一下理財,好好利用這筆錢,但如果小明獲得的金錢數量超過了30萬,他就決定購置一套房屋,但購置房屋的流程是復雜的,小明決定同時完成交首付和貸款的手續。
其實這個流程還不算特別復雜,但到目前為止,單純用一個字段來表明狀態已經無法滿足要求了。
工作流解決的痛點在於,解除業務宏觀流程和微觀邏輯的耦合,讓熟悉宏觀業務流程的人去制定整套流轉邏輯,而讓專業的人只需要關心他們應當關心的流程節點,就好比大家要一起修建一座超級體育場,路人甲只需要關心他身邊的這一堆磚是怎么堆砌而非整座建築。
那么工作流有什么不能解決的問題呢?
工作流是一個固定好的框架,大家就按照這個框架來執行流程就行了,但某些情況他本身沒有流轉順序的要求,比如:小明每天需要做作業,做運動以及玩游戲,它們之間沒有關聯性無法建立流程,但可以根據每一項完成的狀態決定今天的任務是否完結,這種情況我們需要使用CMMN來建模,它就是專門針對這種情況而設計的,但今天我們不講這個,而是講講BPMN協議。
BPMN2.0協議
對於業務建模,我們需要一種通用的語言來描繪,這樣在溝通上和實現上會降低難度,就像中文、英文一樣,BPMN2.0便是一種國際通用的建模語言,他能讓自然人輕松閱讀,更能被計算機所解析。
協議中元素的主要分類為,事件-任務-連線-網關。
一個流程必須包含一個事件(如:開始事件)和至少一個結束(事件)。其中網關的作用是流程流轉邏輯的控制。任務則分很多類型,他們各司其職,所有節點均由連線聯系起來。
下面我就以每種類型的節點簡單地概括一下其作用。
網關:
互斥網關(Exclusive Gateway),又稱排他網關,他有且僅有一個有效出口,可以理解為if......else if...... else if......else,就和我們平時寫代碼的一樣。
並行網關(Parallel Gateway),他的所有出口都會被執行,可以理解為開多線程同時執行多個任務。
包容性網關(Inclusive Gateway),只要滿足條件的出口都會執行,可以理解為 if(......) do, if (......) do, if (......) do,所有的條件判斷都是同級別的。
任務:
BPMN2.0協議的所有任務其實是從一個抽象任務派生而來的,抽象任務會有如下行為:
- 當流程流轉到該任務時,應該做些什么?
- 當該任務獲得信號(signal)的時候,它是否可以繼續向下流轉,而任務獲得信號的這個動作我們稱為Trigger。
利用如上的抽象行為,我們來解釋一些比較常見且具有代表性的任務類型。
人工任務(User Task),它是使用得做多的一種任務類型,他自帶有一些人工任務的變量,例如簽收人(Assignee),簽收人就代表該任務交由誰處理,我們也可以通過某個特定或一系列特定的簽收人來查找待辦任務。利用上面的行為解釋便是,當到達User Task節點的時候,節點設置Assignee變量或等待設置Assignee變量,當任務被完成的時候,我們使用Trigger來要求流程引擎退出該任務,繼續流轉。
服務任務(Service Task),該任務會在到達的時候執行一段自動的邏輯並自動流轉。從“到達自動執行一段邏輯”這里我們就可以發現,服務任務的想象空間就可以非常大,我們可以執行一段計算,執行發送郵件,執行RPC調用,而使用最廣泛的則為HTTP調用,因為HTTP是使用最廣泛的協議之一,它可以解決大部分第三方調用問題,在我們的使用中,HTTP服務任務也被我們單獨剝離出來作為一個特殊任務節點。
接受任務(Receive Task),該任務的名字讓人費解,但它又是最簡單的一種任務,當該任務到達的時候,它不做任何邏輯,而是被動地等待Trigger,它的適用場景往往是一些不明確的阻塞,比如:一個復雜的計算需要等待很多條件,這些條件是需要人為來判斷是否可以執行,而不是直接執行,這個時候,工作人員如果判斷可以繼續了,那么就Trigger一下使其流轉。
調用活動(Call Activity),調用活動可以理解為函數調用,它會引用另外一個流程使之作為子流程運行,調用活動跟函數調用的功能一樣,使流程模塊化,增加復用的可能性。
上面大概介紹了一下常用的節點,下面的圖就展示了一個以BPMN2.0為基礎的流程模型,盡量覆蓋到所介紹的所有節點。
這里是一個生產汽車的流程,從“汽車設計”節點到“批准生產”節點是一個串行的任務,而審批的結果會遇到一個互斥網關,上面講過,互斥網關只需要滿足其中一個條件就會流轉,而這里表達的意義就是審批是否通過。“載入圖紙”是一個服務任務,它是自動執行的,之后會卡在“等待原材料”這個節點,因為這個節點是需要人為去判斷(比如原材料漲價,原材料不足等因素),所以需要在一種自定義的條件下Trigger,而該圖的條件應該為“原材料足夠”,原材料足夠之后,我們會開始並行生產汽車零件。
需要注意的是,並行網關在圖中是成對出現的,他的作用是開始一系列並行任務和等待並行任務一起完成,拿一個Java中的東西舉例子,就是CountDownLatch,fork-join模型也可以類比。
說到這里,網關的底層邏輯我作為拓展提一句,沒聽懂也無傷大雅。網關的本質其實是計數器和出口邏輯的混合,它跟其他節點沒什么區別,只是他的推動邏輯需要使他的計數器為0,而計數器的總數為網關入口線段的數量,比如“組裝”節點前面的並行網關,他的計數器就為4,而前面4個節點,每完成一個就會觸發該網關計數器-1。
當計數器為0的時候,網關會觸發選擇后續流轉的邏輯。
- 互斥網關的邏輯為:遍歷所有出口連線,滿足條件則流出並打斷(也就是break掉)。
- 並行網關的邏輯為:遍歷所有出口連線,無條件為所有連線流出。
- 包容性網關的邏輯為:遍歷所有出口連線,滿足條件的就流出。