作者:李玉亮
JDEasyFlow是企業金融研發部自研的通用流程編排技術元件,適用於服務編排、工作流、審批流等場景,該元件已開源(https://github.com/JDEasyFlow/jd-easyflow),目前在部門的內部業務系統和科技輸出系統中廣泛應用,其他部門也有使用。
它的特點是簡單、靈活、易擴充套件,開發人員一般30分鐘可入門上手,半天可掌握其原理。它分為一個核心模組和若干擴充套件模組,模組之間松耦合,開發使用時可按需選擇、快速整合、漸進式應用,同時支援JSON內建規範和BPMN規範。它的實現原理也有其特色,後面有介紹。
支援的場景功能
節點流轉型別
支援順序流轉、條件流轉、迴圈流轉等。
節點功能型別
支援指令碼節點、使用者節點和訊息節點。
• 指令碼節點:節點執行時執行一段程式碼指令碼
• 使用者節點:根據使用者的操作指令觸發節點執行
• 訊息節點:接收訊息後觸發節點執行
節點串並型別
支援序列執行、並行執行、串並組合執行等。
流程互動場景
支援單次互動一次執行多節點、多次互動一次執行一節點、多次互動一次執行多節點等。
子流程場景
JDEasyFlow支援子流程的場景,可將把複雜的流程節點拆分為子流程,便於業務邏輯抽象。
審批流程場景
JDEasyFlow提供了流程任務審批的能力;常用的審批、撤銷、駁回、會籤、加簽等功能都可支援;內建了簡單的動態表單。既支援從頁面發起和操作流程任務,也支援API的方式,京東OA審批系統也有對接。
功能架構
整體功能架構
JDEasyFlow的功能架構如下圖,功能模組之間松耦合, 開發時可按需選擇、快速整合、漸進式應用。最簡單的使用方式為只在業務應用端引入jar包使用流程引擎。如果需要流程視覺化功能,可整合BPMN規範模組,如果還需要流程例項持久化、流程定義持久化等更豐富功能,則可以整合其他相關模組。
流程引擎模組
JDEasyFlow的核心模組,此模組提供了基於JSON格式的JDEasyFlow規範進行流程編排的能力,其他模組均基於該模組擴充套件,相當於流程執行的發動機、CPU。該功能模組為獨立元件,無資料庫依賴,應用中引入jar包便可使用。
BPMN規範模組
提供了基於BPMN規範進行流程定義和視覺化的能力,流程視覺化基於[bpmn-js](https://bpmn.io/),其本質為提供了將BPMN格式流程定義轉換為JDEasyFlow格式的能力。該模組為獨立元件,僅依賴流程引擎模組,無資料庫和服務依賴,應用中引入jar包便可使用。
目前可支援常用的BPMN元素:
• 任務:指令碼任務、使用者任務、訊息任務
• 事件:開始事件、結束事件、訊息接收事件
• 閘道器:排他閘道器、並行閘道器、包容閘道器
流程定義和例項管理模組
流程定義模組支援流程定義的中心化、版本化管理,流程例項模組支援流程例項的持久化和生命週期管理。該功能依賴資料庫,有服務端和ERP管理端。
任務/審批模組
支援任務生成、任務分配等功能,常用的審批、撤銷、駁回、會籤、加簽等功能都可支援。該功能依賴資料庫,有服務端和ERP管理端。
系統架構
整體系統架構
JDEasyFlow的完整系統架構如下,主要有三個端:業務應用端、流程服務端、流程管理端,三個端可部署在單體應用中,也可分開部署。
中介軟體依賴
· 關係型資料庫(如Mysql)
· 快取(如Redis或R2M)
· 服務通訊框架(如Java API呼叫或Http呼叫或JSF呼叫)
資料庫資料模型比較簡單清晰,見下圖:
效能說明
· 如果僅是服務編排場景,則流程的執行僅依賴記憶體和CPU,並且是在流程客戶端執行,效能上依賴於客戶端伺服器的效能,普通筆記本實測1秒可執行一個流程請求的1w+個節點,1秒可執行1萬+次含1個節點的流程請求
· 如果需要流程狀態管理和流程持久化功能,流程引擎在執行時會到流程服務端查詢和儲存流程例項和流程節點的狀態,效能上主要依賴於資料庫的查詢和插入效率
· 對於流程任務審批功能,流程的任務審批流轉是在服務端執行,一方面取決於流程服務端的計算效能,另一方面同樣取決於資料庫的查詢和插入效率
可伸縮性說明
· 流程引擎屬於無狀態,可隨應用例項線性伸縮
· 流程服務端應用例項支援線性擴充套件
· 流程資料庫可透過分庫分表的方式支援大資料量的增長
實踐建議
在具體實踐中,建議部署統一的流程中心(見下圖),對流程定義統一管理。各系統的應用只需整合流程客戶端jar包進行流程節點開發和流程呼叫便可。如果系統只使用任務審批的功能,則只需要透過API和訊息與流程中心互動便可。
使用示例
流程引擎使用示例
在原始碼的test目錄下有quickstart測試用例(easyflow\easyflow-flow\src\test\java\com\jd\easyflow\flow\quickstart\QuickStartTest.java),可直接執行或除錯以瞭解使用方式和執行原理。具體實踐步驟如下:
1、程式碼中引入easyflow-flow jar包,以maven為例:
<dependency>
<groupId>com.jd.easyflow</groupId>
<artifactId>easyflow-flow</artifactId>
<version>{替換為最新版本}</version>
</dependency>
2、編寫流程定義檔案,以node001->node002→node003的執行順序為例:
{"id": "quickstart_001", "name": "Quick Start 001",
"nodes": [
{"id": "node001","name": "Node001","action": {"createExp": "new com.jd.easyflow.flow.quickstart.QuickStart001Node01Action()"},"start": true,"post": {"to": "node002"}},
{"id": "node002","name": "Node002","action": {"createExp": "new com.jd.easyflow.flow.quickstart.QuickStart002Node01Action()"},"post": {"to": "node003"}},
{"id": "node003","name": "Node003","action": {"createExp": "new com.jd.easyflow.flow.quickstart.QuickStart003Node01Action()"}}
]
}
其中QuickStart001Node01Action等為java節點動作類。完整的流程定義配置項可見: https://github.com/JDEasyFlow/jd-easyflow/wiki/Flow-engine-usage (公網)
3、編寫應用啟動時載入流程引擎的程式碼
FlowEngineImpl flowEngine = new FlowEngineImpl();
flowEngine.setFlowPath("classpath:flow/quickstart/quickstart_001.json");
flowEngine.init();
Spring環境可直接定義FlowEngineImpl bean.
4、編寫具體流程呼叫執行的程式碼
FlowParam param = new FlowParam("quickstart_001");
FlowResult result = flowEngine.execute(param);
完整測試用例的執行結果列印如下:
[main ] INFO FlowEngineImpl - Start parsing definition files:easyflow-flow/target/test-classes/flow/quickstart/quickstart_001.json
[main ] INFO FlowEngineImpl - SART EXECUTE FLOW, flowId:quickstart_001 nodeIds:null
[main ] INFO BaseFlowRunner - EXECUTE NODE:node001
[main ] INFO QuickStart001Node01Action - Execute Node 001
[main ] INFO BaseFlowRunner - NEXT NODES:node002
[main ] INFO BaseFlowRunner - EXECUTE NODE:node002
[main ] INFO QuickStart002Node01Action - Execute Node 002
[main ] INFO BaseFlowRunner - NEXT NODES:node003
[main ] INFO BaseFlowRunner - EXECUTE NODE:node003
[main ] INFO QuickStart003Node01Action - Execute Node 003
[main ] INFO BaseFlowRunner - NEXT NODES:
[main ] INFO QuickStartTest - Execute finish, current node is:node003
BPMN模組使用示例
開啟easyflow-flow-bpmn/BPMNDesigner.html流程設計器. 點選匯入按鈕,匯入easyflow-flow-bpmn/src/test/resources/flow/quickstart/quickstart_001.bpmn檔案,可在設計器中看到和以上JSON定義等價的BPMN流程定義.
程式碼整合使用時只需要將FlowEngineImpl的flowParser設定為BpmnFlowParser.
更多
以上只是流程引擎和BPMN模組的簡單使用示例,JDEasyFlow還包含其他模組、可支援很多的配置項和使用場景,更多使用可見最後的對接使用介紹.
實現原理
目前市面上的流程編排元件基本都是基於圖(邊和頂點)結構的,而本元件是參考了計算機指令執行模型而實現,借鑑了程式計數器的實現原理,引擎內部透過類似程式計數器(PC)的待執行節點棧來維護後繼節點;可以理解為是一種高階業務程式語言,它同時也是圖靈完備的。
流程引擎核心模型名詞只有一個:節點(Node),節點的功能為執行邏輯並輸出後續節點 。
開發態可定義有限的節點,透過每個節點與其後續節點連線形成有向圖;執行態按規則邏輯進行節點流轉,支援並行執行,支援順序、條件或迴圈,支援fork-join。
概念:
• 流程:一個業務流程的抽象
• 節點:流程的組成單位,一個節點能夠執行節點動作同時可返回後繼節點
節點內部構件:
節點內部構件的組成是可自定義的,流程引擎提供了預設實現,其內部構件包括了前處理器(PreHandler)、節點動作(NodeAction)、後處理器(PostHandler)
• 前處理器:判斷該節點是否可以執行動作
• 節點動作:真實的業務功能處理
• 後處理器:負責計算後續節點
流程引擎執行邏輯
流程引擎有一個或多個流程觸發節點,流程觸發後執行如下邏輯:
1. 初始化流程上下文
2. 得到流程起始節點ID,放入執行棧
3. 如果執行棧為空,則返回,否則執行當前節點
-
預檢查
-
執行Action
-
計算後繼節點ID並返回
4. 將後繼節點放入執行棧,從棧中取出待執行節點,跳到第3步
因此JDEasyFlow整體的特色為簡單:
• 模型簡單:核心模型概念就是節點的流轉
• 擴充套件簡單:提供了監聽器、過濾器功能,方便橫向切面;節點支援自定義實現
• 定義簡單:只需要透過JSON進行節點流轉邏輯配置便可,也支援BPMN格式
• 執行簡單:程式碼呼叫流程引擎,傳入流程ID和業務引數便可
• 使用簡單:引入元件包便可使用,比較輕量
適用場景和對接使用說明
適用場景
理論上JDEasyFlow可滿足任何流程場景,它主要可解決三類問題:
• 流程可編排:將業務流程抽象為軟體流程,保證軟體是現實的真實反映;不同場景可定義不同流程,且流程易修改
• 功能松耦合:將業務節點抽象為軟體流程節點,一方面實現功能的松耦合,另一方面實現節點的可複用
• 流程視覺化:所見即所得,方便業務產品人員和軟體研發人員基於同一語言的交流,也便於流程監控
在實際軟體系統開發過程中,如果有如下訴求,可考慮使用流程編排:
• 業務流程是有明顯的多個節點組成
• 希望流程可靈活變更
• 業務流程級別比程式流程高一層,在程式語言級別難以聚合和治理(如一個流程即需要前臺操作,又有外系統參與,又有後臺操作,在實現上入口分散)
對接使用
JDEasyFlow的所有文件可見: https://github.com/JDEasyFlow/jd-easyflow/wiki (公網)
歡迎大家對接使用,有相關使用問題可聯絡: liyuliang5@jd.com