Activiti工作流與業務整合實戰

2020-12-11 全棧技術資源社區

1. 業務背景

項目開發過程中如果均是簡單的數據請求與返回,那麼方法調用和業務邏輯是最容易處理的,根據入參返回數據即可,數據的生命周期始於請求,終於數據返回,沒有其他。

倘若特定需求場景需要多個接口協作完成一件事,數據流轉存在多路由,業務邏輯處理將會呈現複雜化,樸素的數據流控制方式就是定義數據中間態通過硬編碼形式來影響數據流向,這種設計在複雜度不深的情況下總是很容易實現,開發成本和溝通成本較低,也不失為一種非常有效的開發設計方式。而隨著業務場景複雜化,流程變更頻繁,開發人員會在之前得益的簡單設計上發現維護和可拓展性極差,甚至陷入流程泥潭中難以自救,最直接的表現就是接口交互定製化,所有的交互看不到任何業務或故事主線,所有的服務交互都需要最原始的那些開發人員的文檔、

注釋甚至「言傳身教」的指導才可以洞察複雜業務的其中一二,這是軟體開發中的技術負債和不完善,我們急需一個可以引導完整業務流程的體系或者框架來引導服務交互,來驅動業務數據流轉,對數據的出生、中轉、停留及最終消亡進行有效控制和監管,讓服務有源可溯,有序可遵。關於以上概述都是為了引申出下面項目實踐的利器,工作流。

數據流轉依賴硬編碼,面向接口交互,沒有統一司令塔服務進行調度面向業務數據設計,複雜邏輯的業務中,數據流故事主線不清晰,無法監控也無從溯源

2.技術調研

JBPM vs Activiti選型對比

關於工作流開源框架,一般有JBPM和Activiti,簡單檢索了下兩者對比如下:

Activiti持久層通過MyBatis實現、與Spring融合支持事務,與當前項目技術背景較為符合,且上手較為便捷,參考資料廣泛,學習成本較低,加上之前個人項目運用過Activiti前身,對PVM設計模式有一定了解,最終決定採用Activiti作為工作流來進行開發。

Activiti工作流特點

關於Activiti工作流的具體內容這裡不做贅述,本文的核心放在Activiti工作流與業務結合的實踐,下面是Activiti工作流的一些特點:

數據持久化支持鏈式API編程風格,所有的編程參與對象都可通過ProcessEngine獲取到支持流程設計器。可以結合IDEA中的ActiBpm等插件進行可視化流程設計,它最終轉換的是bpm文件,是一個類xml的流程配置文件原生支持Spring分離運行時與歷史數據Activiti是基於單庫單表的持久化3.流程設計

目前負責的項目是一個關於用戶認證相關的業務,簡單描述認證業務流程如下:

工作流起始 工作流的開始和結束,是整個工作流程的起點和重點工作流節點 工作流的核心節點和銜接,每個節點是故事主線的主要構成部分,代表一個聚合的業務邏輯,每個節點根據預定義走向進行數據驅動工作流路由 工作流路由分發,根據前置數據來進行工作流走向的決策,從而影響後續節點的流轉數據中間態 除了開始、結束兩個節點,被虛線包括的部分都是數據的中間態,無論業務數據和工作流數據,此時呈現的最大特點就是數據的不穩定性,業務處理中隨時都可能根據外部業務驅動產生數據和業務流程的向下繼續、向上回溯、分叉決策等,也是工作流中最活躍的部分4.架構設計

Service業務邏輯層

Activiti Service 封裝了工作流基礎API,這裡主要用到Activiti的RepositoryService、RuntimeService、TaskService三個服務類,RepositoryService提供了服務流程部署功能,RuntimeService提供了運行時服務這裡主要涉及任務啟動,TaskService提供了任務完成、任務取消等流程驅動的方法支持。這裡是基於Activiti原生API進行了定製化任務支持,類似一個門面服務,主要是用來融合Activiti原生服務和實際項目業務需求的APIWorkFlowAspect 通過Aop切面將工作流邏輯進行抽離,保證與業務層具體邏輯方法的隔離實現解耦;調用底層Activiti Service是提供的服務接口完成工作流驅動@WorkFlowHandle 通過Spel解析Annotation註解形參,支持Map類型複雜數據結構傳參,結合Aop進行實參映射綁定,解決切面層與業務層參數傳遞問題;通過@Repeatable支持業務方法的重複註解,為工作流作用業務方法的靈活配置的可擴展提供支持Dao數據持久層

水平拆分 根據業務數據的庫表設計進行工作流持久化改造,按照用戶userId進行工作流數據水平拆分事務支持 由於切分鍵一致,使得工作流數據與業務數據分離的同時能夠支持資料庫事務,保證數據完整一致性,減少開發複雜度5.項目實戰

5.1 maven配置

目前Activiti已進入7.0.0+,翻閱了大量網上資料,該版本輸出的時間較少,大部分還集中在5或6,這裡基於可用可操作的思想選用了介紹參考資料詳實的5.22.0進行開發,每個大版本變更差異較大,其他版本根據實踐需要進行升級或取捨

<activiti.version>5.22.0</activiti.version>

<!-- activiti -->

<dependency>

<groupId>org.activiti</groupId>

<artifactId>activiti-engine</artifactId>

<version>${activiti.version}</version>

</dependency>

<artifactId>activiti-spring</artifactId>

5.2 持久化改造

分庫分表

Activiti默認是單庫單表的,而我們現有項目是分庫分表的,路由策略是根據用戶ID進行水平切分的。

對Activiti的持久化存儲進行分庫分表改造基於以下兩點考慮:

業務數據分庫分表,Activiti工作流數據的流轉肯定是要和業務數據做聯動的,按照同樣語義即用戶ID進行水平切換路由可以輕鬆實現資料庫事務,減少實現數據一致性帶來的設計問題。業務表做了分庫分表的原因就是用戶體量大,持久化數據龐大,儘管讀多寫少且寫入量單庫單表在一段時間內不會對業務性能產生絲毫影響,但是為了擴展性更好,也為了規避後續變更難度,決定對Activiti工作流數據持久化也進行水平拆分。欄位及索引長度適配

為了保證數據完整性,Activiti默認創建表存在大量的外鍵約束,在生產環境下可以根據自身開發需要對這些外鍵進行去除,從而提高庫表查詢和處理性能。關於數據完整性可以通過應用程式層面進行保證和處理。Activiti的庫表設計都是基於可用性和普遍適配性進行設計的,個別欄位如存在varchar(4000)來記錄異常信息,根據自身業務存儲及性能考慮進行了部分欄位基於可用性的縮減;還有部分表存在大量的聯合索引,由於欄位長度過長導致聯合索引存儲大於736位元組,這是MySQL官方推薦的索引最大值,超過該值可能會產生性能問題,項目中沒有對MySQL默認限制進行修改,而是根據項目情況縮減了聯合索引欄位的長度。5.3 部署bpmn更新問題

在實際項目環境下,沒有bpmn流程調整我們是不需要頻繁進行bpmn流程部署的,每次新流程部署都會更新刷下流程ID、實例ID等,而且數據也會產生變更調整,而我們的需求是在需要更新的時候更新,不需要更新的時候覆用即可。因此在服務啟動時,我這裡通過時間戳作為版本號,每次啟動Spring服務時進行bpmn部署檢查,如果應用中的版本號未進行變更則不刷新Activiti部署信息保持之前部署快照,否則創建並部署新bpmn流程進行部署創建,更新部署快照信息,下面放一段demo代碼,由於項目是分庫分表的,每次啟動服務會輪詢每個分庫的deploy信息情況。

/**

* @author: guanjian

* @date: 2020/11/24 19:48

* @description: Activiti部署

*/

@Component("activitiDeployServlet")

public class ActivitiDeployServlet {

private final static Logger LOGGER = LoggerFactory.getLogger(ActivitiDeployServlet.class);

@Autowired

private RepositoryService repositoryService;

private BaseDBRouter dbRouter;

* BPMN流程定義部署

*

* @desc <p>

* 1、根據分庫數量(dbRouter.getDbNumber)進行bpmn流程定義部署

* 2、根據WORK_FLOW_DEPLOY進行部署,每次部署進行防重判斷,變更WORK_FLOW_DEPLOY則重新部署即啟用新流程

* WORK_FLOW_DEPLOY一致則不進行重新部署,復用原流程

* <p>

@PostConstruct

public void deploy() {

Optional.ofNullable(dbRouter.getDbKeyArray())

.orElse(Lists.newArrayList())

.forEach(db->{

try {

DBContextHolder.setDBKeyIndex(db);

handleDeployBpmn();

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException("check deploy bpmn error.");

} finally {

DBContextHolder.clearDBKeyIndex();

}

});

private Deployment queryBpmn() {

return repositoryService.createDeploymentQuery()

.deploymentName(ActivitiConstants.Deployment.WORK_FLOW_DEPLOY)

.singleResult();

private void handleDeployBpmn() {

Deployment deployment = queryBpmn();

if (Optional.ofNullable(deployment).isPresent()) {

LOGGER.info("{} bpmn exists.", DBContextHolder.getDBKeyIndex());

} else {

createBpmn();

private void createBpmn() {

repositoryService.createDeployment()

.disableSchemaValidation()

.name(ActivitiConstants.Deployment.WORK_FLOW_DEPLOY)

.addClasspathResource(ActivitiConstants.Deployment.BPMN_RESOURCE_PATH)

.deploy();

LOGGER.info("{} bpmn created.", DBContextHolder.getDBKeyIndex());

5.4 Activiti核心服務

Activiti主要有以下幾個核心服務,我們這裡最重要的是使用到了RepositoryService、RuntimeService、TaskService,RepositoryService用來進行服務部署,RuntimeService用來啟動任務管理任務實例,TaskService用來進行任務的查詢、流轉(完成、取消)等操作。

5.5 整合Spring

與Spring整合後,可以通過Spring來操作配置Activiti

<!-- Activiti processEngineConfiguration -->

<bean id="processEngineConfiguration">

<property name="dataSource" ref="routerTargetDataSource"/>

<property name="transactionManager" ref="routerTransactionManager"/>

<!--

flase:默認值。activiti在啟動時,會對比資料庫表中保存的版本,如果沒有表或者版本不匹配,將拋出異常。(生產環境常用)

true:activiti會對資料庫中所有表進行更新操作。如果表不存在,則自動創建。(開發時常用)

create_drop:在activiti啟動時創建表,在關閉時刪除表(必須手動關閉引擎,才能刪除表)。(單元測試常用)

drop-create:在activiti啟動時刪除原來的舊錶,然後在創建新表(不需要手動關閉引擎)。

-->

<property name="databaseSchemaUpdate" value="true"/>

<property name="history" value="none"/>

</bean>

<!-- Activiti processEngine -->

<bean id="processEngine">

<property name="processEngineConfiguration" ref="processEngineConfiguration"/>

<!-- Activiti Service -->

<bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/>

<bean id="runtimeService" factory-bean="processEngine" factory-method="getRuntimeService"/>

<bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/>

<bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>

<bean id="managementService" factory-bean="processEngine" factory-method="getManagementService"/>

</beans>

5.6 工作流常量定義

private final static String BPMN_DEPLOY_PATH_TEMPLATE = "bpmn/*.bpmn";

* 部署資源

public static class Deployment {

//BPMN流程定義名稱

public final static String WORK_FLOW_KEY = "xxx_workflow";

//BPMN流程版本號(yyyyMMddHHmm)

public final static String WORK_FLOW_VERSION = "v202012031347";

//BPMN流程部署名稱(名稱_版本號)

//每次部署對該變量進行防重判斷,版本號變更則部署新流程進行新部署

public final static String WORK_FLOW_DEPLOY = WORK_FLOW_KEY + Constants.Symbol.LINE + WORK_FLOW_VERSION;

//BPMN文件讀取路徑

public final static String BPMN_RESOURCE_PATH = BPMN_DEPLOY_PATH_TEMPLATE.replace("*", WORK_FLOW_KEY);

* 工作流節點定義

public enum WorkFlowNodeEnum implements SingleItem<String> {

* 起始

//開始

START("START"),

//結束

END("END"),

* 普通節點

NODE_1("NODE_1"),

NODE_2("NODE_2"),

NODE_3("NODE_3"),

* 排他網關

//數據源

XOR_DATA_SOURCE("XOR_dataSource"),

//反饋結果

XOR_CHSI_RESULT("XOR_chsiResult"),

;

private String key;

WorkFlowNodeEnum(String key) {

this.key = key;

@Override

public String getKey() {

return key;

public void setKey(String key) {

* 流程變量

public static class Variables {

//用戶ID

public final static String USER_ID = "userId";

//業務ID

public final static String BIZ_ID = "bizId";

public final static String DATASOURCE = "dataSource";

public final static String RESULT= "result";

* 任務取消原因

public static class CancelReason {

//強制作廢流程

public final static FINISH_FORCE_INVALID = "FINISH_FORCE_INVALID";

* 任務操作枚舉

public enum TaskOperateEnum {

//任務啟動

START,

//任務取消

CANCEL,

//任務完成

COMPLETE,

5.7 工作流註解@WorkFlowHandle & @WorkFlowHandles

@WorkFlowHandle是支持工作流操作的註解,@WorkFlowHandles則是藉助JDK1.8的@Repeatable特性支持重複註解,使得@WorkFlowHandle可以重複作用在業務Service方法上進行工作流操作。

@WorkFlowHandle的定義如下:

這裡的註解欄位定義可以根據具體業務進行抽象和具體,以上僅供參考。

* @date: 2020/12/02 17:14

* @description: 工作流處理

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Repeatable(WorkFlowHandles.class)

public @interface WorkFlowHandle {

* 任務委託人

String assignee() default "";

* 業務ID

String bizId() default "";

* 環境變量

String variables() default "";

* 任務操作

ActivitiConstants.TaskOperateEnum taskOperate();

* 取消原因

String cancelReason() default "";

* 任務節點

ActivitiConstants.WorkFlowNodeEnum node() default ActivitiConstants.WorkFlowNodeEnum.END;

public @interface WorkFlowHandles {

WorkFlowHandle[] value();

5.8 工作流切面 WorkFlowAspect

WorkFlowAspect是以切面形式存在的工作流業務,把涉及工作流相關的元素數據抽離到切面中進行集中處理,通過註冊與業務邏輯進行融合,代碼隔離減少了邏輯耦合和混雜,通過以下腳手架的拼接完成了該功能的實現,匯總如下:

5.9 工作流與業務Service融合

@WorkFlowHandle(taskOperate = ActivitiConstants.TaskOperateEnum.START,

assignee = "#req.userId",

bizId = "#req.uuid",

variables = "{userId:'#req.userId',dataSource:'#req.dataSource'}"

)

@WorkFlowHandle(taskOperate = ActivitiConstants.TaskOperateEnum.COMPLETE,

node = ActivitiConstants.WorkFlowNodeEnum.NODE_1

@WorkFlowHandle(taskOperate = ActivitiConstants.TaskOperateEnum.CANCEL,

assignee = "#req.userId"

public Result bizMethod(Req req) {

//業務操作

//do something

return Result.success();

6. 其他

表刪除順序

由於存在外鍵,表刪除需要按照以下順序進行,第一遍沒刪除掉,可以再來一遍就乾淨了,測試環境使用的備記下

DROP TABLE IF EXISTS `ACT_RU_VARIABLE`;

DROP TABLE IF EXISTS `ACT_RU_EXECUTION`;

DROP TABLE IF EXISTS `ACT_RE_PROCDEF`;

DROP TABLE IF EXISTS `ACT_ID_GROUP`;

DROP TABLE IF EXISTS `ACT_GE_BYTEARRAY`;

DROP TABLE IF EXISTS `ACT_RE_DEPLOYMENT`;

DROP TABLE IF EXISTS `ACT_EVT_LOG`;

DROP TABLE IF EXISTS `ACT_GE_PROPERTY`;

DROP TABLE IF EXISTS `ACT_HI_ACTINST`;

DROP TABLE IF EXISTS `ACT_HI_ATTACHMENT`;

DROP TABLE IF EXISTS `ACT_HI_COMMENT`;

DROP TABLE IF EXISTS `ACT_HI_DETAIL`;

DROP TABLE IF EXISTS `ACT_HI_IDENTITYLINK`;

DROP TABLE IF EXISTS `ACT_HI_PROCINST`;

DROP TABLE IF EXISTS `ACT_HI_TASKINST`;

DROP TABLE IF EXISTS `ACT_HI_VARINST`;

DROP TABLE IF EXISTS `ACT_ID_INFO`;

DROP TABLE IF EXISTS `ACT_ID_MEMBERSHIP`;

DROP TABLE IF EXISTS `ACT_ID_USER`;

DROP TABLE IF EXISTS `ACT_PROCDEF_INFO`;

DROP TABLE IF EXISTS `ACT_RE_MODEL`;

DROP TABLE IF EXISTS `ACT_RU_EVENT_SUBSCR`;

DROP TABLE IF EXISTS `ACT_RU_IDENTITYLINK`;

DROP TABLE IF EXISTS `ACT_RU_JOB`;

DROP TABLE IF EXISTS `ACT_RU_TASK`;

相關焦點

  • 「Activiti精品 悟纖出品」activiti介紹-十萬個為什麼 - 第321篇
    「超詳細,值得收藏」看不懂還有配套視頻 - 第319篇「工作流Activiti」介紹-新手上路,注意安全 - 第320篇師傅:上一節,我們對於Activiti要了一個基本的認識了,這一節我們來…悟纖:(不等師傅說完)是不是這一節要來實戰下了。
  • 最簡單的Activiti入門教程
    工作流(Workflow),就是「業務過程的部分或整體在計算機應用環境下的自動化」,它主要解決的是「使在多個參與者之間按照某種預定義的規則傳遞文檔、信息或任務的過程自動進行,從而實現某個預期的業務目標,或者促使此目標的實現」這裡我們也可以比對出工作流的優點:提高系統的柔性,適應業務流程的變化實現更好的業務過程控制,提高顧客服務質量降低系統開發和維護成本
  • Activiti學習詳解【面試+工作】
    工作流(Workflow),就是「業務過程的部分或整體在計算機應用環境下的自動化」,它主要解決的是「使在多個參與者之間按照某種預定義的規則傳遞文檔、信息或任務的過程自動進行,從而實現某個預期的業務目標,或者促使此目標的實現
  • Activiti 5.17.0 發布,新增 Async executor
    Modeler修訂了用戶指南,重寫了 AsciiDocIt's now possible to start a process instance a skip tasks based on a skipExpressionWe added a new module named activiti
  • 整合activity工作流引擎的圖形化流程設計器
    工作流引擎包括流程的節點管理、流向管理、流程樣例管理等重要功能。簡單來說,就是為了統一管理流程業務。想想看,如果要設計一個流程的程序,通常需要在資料庫中存各種狀態值,比如一個訂單程序,要標記訂單是未付款、已付款、已出庫等等狀態,而這些各種各樣的狀態參雜在程序中,邏輯自然就變得複雜了。
  • java快速開發平臺之多系統快速配置工作流引擎
    工作流引擎使得構建在各種這樣的系統中的服務,可以以一種統一和通用的方式進行交互。例如開發一個系統,最關鍵的部分不是系統的界面,也不是和資料庫之間的信息交換,而是如何根據業務邏輯開發出符合實際需要的程序邏輯並確保其穩定性、易維護性(模塊化和結構化)和彈性(容易根據實際業務邏輯的變化作出程序上的變動,例如決策權的改變、組織結構的變動和由於業務方向的變化產生的全新業務邏輯等等)。
  • Jira實戰 | 創建你的工作流
    測試、代碼審查和部署的狀態可能需要較為複雜的工作流。 命名你的工作流工作流名稱應該用來描述整個生命周期過程的類型,而不是用項目名稱來命名,例如: 「任務生命周期」。工作流方案的名稱應該能夠描述生命周期流程群組的類型,例如:「研發生命周期」。狀態名稱應該要精簡併且能夠反應當前狀況。
  • 慧正通軟技術副總裁:工作流引擎廠商的生存之道
    管理需要流程化、流程需要制度化;基于慧正工作流引擎的工作流系統包括引擎、管理工具、設計工具、應用工具共同組成了一條「業務流程快速生產線」,可以實現信息化項目模式的創新和轉變。集成商可以先建議用戶採購「業務流程快速生產線」後,搭建兩條示範流程,取得效果後再分期分批建設業務流程管理系統。這樣做的好處是業務流程信息化的價值和產值都可以充分體現出來。
  • 華東師範大學:以數據流視角打造工作流
    華東師範大學將工作流引入信息系統建設中,通過「流程再造」,重新梳理各管理部門間的業務流程,這是高校信息系統建設的一種新視角。  本文從高校信息系統建設角度入手,將工作流引入高校信息系統建設中。通過重新梳理設計多個業務部門的業務流程,利用流程優化中的工作流技術,使高校各部門間的信息系統實現無縫集成。加強部門間信息系統的數據共享,提高高校各部門的業務執行效率。以華東師範大學研究生出國(境)申請系統為例,剖析基於工作流的高校信息系統建設過程中技術實現思路,以及實施過程中遇到的問題和對策。
  • Activiti快速入門Demo kft-activiti-demo 1.10 發布
    kft-activiti-demo 1.10 發布,改進內容包括:集成了diagram-viewer
  • Activiti 5.21.0 發布,業務流程管理 - OSCHINA - 中文開源技術...
    Data Object support for Activiti Modeler[ACT-2073] - Document custom identity link feature in User Guide[ACT-2074] - add support for custom identity links to Modeler詳情:http://www.activiti.org
  • 西安交通大學:「工作流」讓報修與接單更智能
    技術背景  工作流  工作流,顧名思義,就是業務信息數據在多個環節模塊之間的流轉。按照工作流管理聯盟的定義,工作流指的是「業務過程的部分或全部在計算機應用環境下的自動化」。在實際應用過程中,為了實現對業務過程的工作流管理,需要對業務流程及其各個步驟之間業務規則進行抽象,概括,做成一個統一通用的流程管理軟體系統,這種軟體系統就是工作流管理系統。  工作流管理系統的主要功能是通過計算機技術的支持去定義、執行和管理工作流,協調工作流執行過程中群體成員之間的信息交互。
  • net core快速開發平臺,learun自主工作流引擎設計規範
    工作流定義文件描述了業務的交互邏輯,工作流引擎通過解析此工作流定義文件按照業務的交互邏輯進行業務的流轉,工作流引擎通常通過參考某種模型來進行設計,通過調度算法來進行流程的流轉(流程的啟動、終止、掛起、恢復等),通過各種環節調度算法(SPLIT、AND、OR等)來實現對於環節的流轉(環節的合併、分叉、選擇、條件性的選擇等)。
  • Gfast 工作流引擎 1.0 正式發布
    Gfast管理系統採用go語言開發,基於GF(Go Frame)的後臺管理系統,現增加工作流引擎模塊開源發布,實現工作流引擎與業務解綁
  • 阿里雲Serverless工作流正式商用,輕鬆擁有雲上自動生產線
    Serverless工作流來了!發布會傳送門搶先了解Serverless技術乾貨4月,阿里雲Serverless工作流正式商業化,這是一款用於協調多個分布式任務執行的全託管 Serverless 雲服務。
  • 南洋科技事件點評:啟動業務內部整合,計劃更名重新啟航
    會議審議通過了《關於公司實施業務整合的議案》、《關於變更公司名稱的議案》和《關於變更公司證券簡稱的議案》等事項。啟動業務內部整合現有膜業務穩定發展根據本次會議審議通過的《關於公司實施業務整合的議案》,公司計劃將目前的主營業務,即電容器膜、能電池背材膜、光學膜、鋰離子電池隔膜等膜業務進行整合,全部膜業務相關的資產、負債、業務及人員等整體劃轉至子公司浙江南洋信通新材料有限公司。
  • 後臺系統設計:工作流設計剖析
    設計好一個後臺工作流,不僅可以使得後期使用系統的時候更加高效,同時也是十分考驗產品經理的。剛好最近自己在做這方面的工作,所以總結了一些方法經驗與大家共勉。一、了解什麼是工作流及工作流的類型在企業級的一些系統中,工作流是非常常見的一個輔助功能,因為許多操作是無法通過操作者一個人來完成的。