工作中最常用的五種工作流引擎
前言
有些小伙伴在工作中,一提到工作流引擎就頭疼,不知道如何選型,更不清楚它們底層的原理。
其實(shí),工作流引擎就像我們生活中的交通信號(hào)系統(tǒng),它負(fù)責(zé)協(xié)調(diào)各個(gè)"路口"(業(yè)務(wù)節(jié)點(diǎn))的通行順序,確保整個(gè)"交通"(業(yè)務(wù)流程)有序進(jìn)行。
今天我想和大家聊聊工作中最常用的5種工作流引擎,希望對(duì)你會(huì)有所幫助。
一、工作流引擎是什么?
在深入具體引擎之前,我們先明確一個(gè)基本概念:工作流引擎本質(zhì)上是業(yè)務(wù)流程自動(dòng)化的核心支撐系統(tǒng)。
它負(fù)責(zé)將復(fù)雜的業(yè)務(wù)邏輯分解為多個(gè)任務(wù)節(jié)點(diǎn),并控制這些節(jié)點(diǎn)之間的流轉(zhuǎn)邏輯。
舉個(gè)例子,比如一個(gè)請(qǐng)假審批流程:?jiǎn)T工提交申請(qǐng) → 直接主管審批 → 部門經(jīng)理審批 → HR備案 → 結(jié)束。
如果每個(gè)環(huán)節(jié)都用if-else硬編碼,代碼會(huì)變得極其臃腫且難以維護(hù)。
而工作流引擎通過可視化的方式定義流程,讓業(yè)務(wù)邏輯和流程控制徹底解耦。
為了讓大家更直觀地理解工作流引擎的架構(gòu),我畫了一個(gè)整體架構(gòu)圖:
圖片
這張圖展示了工作流引擎的核心組件。接下來,我們看看為什么要用工作流引擎:
- 降低復(fù)雜度:將業(yè)務(wù)流程從業(yè)務(wù)代碼中解耦
- 提高可維護(hù)性:流程變更只需修改配置,無需改代碼
- 增強(qiáng)可視化:大多數(shù)引擎提供流程設(shè)計(jì)器和監(jiān)控界面
- 保證一致性:通過狀態(tài)機(jī)保證業(yè)務(wù)流程的標(biāo)準(zhǔn)執(zhí)行
有些小伙伴在工作中可能會(huì)問:"我們系統(tǒng)業(yè)務(wù)邏輯不復(fù)雜,需要用工作流引擎嗎?"
我的經(jīng)驗(yàn)是:當(dāng)你的業(yè)務(wù)有狀態(tài)流轉(zhuǎn)、多人協(xié)作、需要長(zhǎng)時(shí)間運(yùn)行的特征時(shí),就應(yīng)該考慮使用工作流引擎了。
二、Activiti:企業(yè)級(jí)標(biāo)準(zhǔn)工作流引擎
Activiti可以說是Java領(lǐng)域最老牌、最知名的工作流引擎之一,它最初是jBPM的原作者離開JBoss后創(chuàng)建的。
核心原理
Activiti基于BPMN 2.0標(biāo)準(zhǔn),采用狀態(tài)機(jī)+命令模式的設(shè)計(jì)。
它的核心思想是將業(yè)務(wù)流程抽象為一系列的活動(dòng)節(jié)點(diǎn),通過令牌(Token)在節(jié)點(diǎn)間的流動(dòng)來驅(qū)動(dòng)流程執(zhí)行。
架構(gòu)特點(diǎn):
- 支持完整的BPMN 2.0元素
- 采用命令模式,所有操作都封裝為命令
- 基于狀態(tài)機(jī)管理流程實(shí)例狀態(tài)
- 支持事務(wù)性流程執(zhí)行
開源地址
GitHub: https://github.com/Activiti/Activiti
示例:請(qǐng)假審批流程
首先在Spring Boot項(xiàng)目中引入依賴:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M6</version>
</dependency>定義BPMN 2.0流程文件(leave-approval.bpmn20.xml):
<?xml versinotallow="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
<process id="leaveApproval" name="請(qǐng)假審批流程" isExecutable="true">
<!-- 開始事件 -->
<startEvent id="startEvent" />
<!-- 用戶提交申請(qǐng) -->
<userTask id="submitLeave" name="提交請(qǐng)假申請(qǐng)"
activiti:assignee="#{employee}"/>
<!-- 主管審批 -->
<userTask id="leaderApproval" name="直接主管審批"
activiti:assignee="#{leader}"/>
<!-- 排他網(wǎng)關(guān):根據(jù)審批結(jié)果決定流向 -->
<exclusiveGateway id="decisionGateway"/>
<!-- 條件序列流:審批通過 -->
<sequenceFlow id="flowApproved" sourceRef="decisionGateway" targetRef="hrRecord">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${approved == true}]]>
</conditionExpression>
</sequenceFlow>
<!-- 條件序列流:審批駁回 -->
<sequenceFlow id="flowRejected" sourceRef="decisionGateway" targetRef="rejectEnd">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${approved == false}]]>
</conditionExpression>
</sequenceFlow>
<!-- HR備案 -->
<userTask id="hrRecord" name="HR備案"
activiti:assignee="#{hr}"/>
<!-- 結(jié)束事件 -->
<endEvent id="endEvent" />
</process>
</definitions>Java代碼中啟動(dòng)和執(zhí)行流程:
@Service
@Transactional
publicclass LeaveProcessService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
// 部署流程定義
public void deployProcess() {
repositoryService.createDeployment()
.addClasspathResource("processes/leave-approval.bpmn20.xml")
.name("請(qǐng)假審批流程")
.deploy();
}
// 啟動(dòng)請(qǐng)假流程
public void startLeaveProcess(String employee, String leader, int leaveDays) {
Map<String, Object> variables = new HashMap<>();
variables.put("employee", employee);
variables.put("leader", leader);
variables.put("hr", "hr_department");
variables.put("leaveDays", leaveDays);
// 啟動(dòng)時(shí)尚未有審批結(jié)果,approved變量稍后設(shè)置
variables.put("approved", null);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
"leaveApproval", variables);
System.out.println("流程啟動(dòng)成功,實(shí)例ID:" + processInstance.getId());
}
// 查詢用戶待辦任務(wù)
public List<Task> getPendingTasks(String assignee) {
return taskService.createTaskQuery()
.taskAssignee(assignee)
.orderByTaskCreateTime().desc()
.list();
}
// 完成任務(wù)并傳遞審批結(jié)果
public void completeTask(String taskId, boolean approved, String comment) {
Map<String, Object> variables = new HashMap<>();
variables.put("approved", approved);
// 添加審批意見
if (comment != null && !comment.trim().isEmpty()) {
taskService.addComment(taskId, null, comment);
}
taskService.complete(taskId, variables);
System.out.println("任務(wù)完成,審批結(jié)果:" + (approved ? "通過" : "駁回"));
}
}代碼邏輯深度解析:
- 流程定義:BPMN文件定義了節(jié)點(diǎn)拓?fù)浣Y(jié)構(gòu),
userTask表示人工任務(wù),exclusiveGateway是決策點(diǎn) - 變量傳遞:通過
variables映射表在流程實(shí)例中傳遞業(yè)務(wù)數(shù)據(jù) - 任務(wù)查詢:Activiti自動(dòng)維護(hù)任務(wù)狀態(tài),通過
TaskQueryAPI查詢待辦 - 狀態(tài)持久化:所有流程狀態(tài)自動(dòng)持久化到數(shù)據(jù)庫(kù),保證一致性
有些小伙伴在工作中可能會(huì)遇到Activiti表太多的問題,其實(shí)這是因?yàn)樗捎?/span>分表設(shè)計(jì):ACT_RE_*存儲(chǔ)靜態(tài)定義,ACT_RU_*存儲(chǔ)運(yùn)行時(shí)數(shù)據(jù),ACT_HI_*存儲(chǔ)歷史數(shù)據(jù)。
這種設(shè)計(jì)既保證了運(yùn)行時(shí)性能,又支持歷史追溯。
三、Flowable:Activiti的性能優(yōu)化版
Flowable是Activiti的原班人馬創(chuàng)建的分支項(xiàng)目,在性能和云原生支持方面有顯著改進(jìn)。
它完全兼容Activiti的API,但內(nèi)部進(jìn)行了大量?jī)?yōu)化。
核心原理
Flowable在Activiti的基礎(chǔ)上引入了異步執(zhí)行器和執(zhí)行樹優(yōu)化。
它通過更精細(xì)的鎖管理和批量處理來提升高并發(fā)下的性能。
架構(gòu)改進(jìn):
- 增強(qiáng)的異步執(zhí)行器,支持批量操作
- 優(yōu)化的執(zhí)行樹結(jié)構(gòu),減少數(shù)據(jù)庫(kù)訪問
- 更好的緩存管理和懶加載策略
- 原生支持Spring Boot Starter
開源地址
GitHub: https://github.com/flowable/flowable-engine
示例:訂單處理流程(含服務(wù)任務(wù))
Flowable特別擅長(zhǎng)處理自動(dòng)化業(yè)務(wù)流程,下面是一個(gè)訂單處理示例:
@Service
publicclass OrderProcessService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private RepositoryService repositoryService;
// 部署訂單流程
public void deployOrderProcess() {
repositoryService.createDeployment()
.addClasspathResource("processes/order-process.bpmn20.xml")
.addClasspathResource("processes/order-discount.dmn") // DMN決策表
.deploy();
}
// 啟動(dòng)訂單流程
public void startOrderProcess(Order order) {
Map<String, Object> variables = new HashMap<>();
variables.put("order", order);
variables.put("customerService", "cs_team");
variables.put("warehouse", "wh_team");
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
"orderProcess", order.getOrderNo(), variables);
logger.info("訂單流程啟動(dòng): {}", processInstance.getId());
}
}
// 服務(wù)任務(wù)處理器 - 庫(kù)存檢查
@Component
publicclass InventoryCheckDelegate implements JavaDelegate {
@Autowired
private InventoryService inventoryService;
@Override
public void execute(DelegateExecution execution) {
Order order = (Order) execution.getVariable("order");
// 調(diào)用庫(kù)存服務(wù)檢查庫(kù)存
boolean inStock = inventoryService.checkInventory(
order.getProductId(), order.getQuantity());
execution.setVariable("inStock", inStock);
if (!inStock) {
execution.setVariable("needBackorder", true);
logger.warn("產(chǎn)品{}庫(kù)存不足,需要備貨", order.getProductId());
}
}
}
// 服務(wù)任務(wù)處理器 - 支付處理
@Component
publicclass PaymentProcessDelegate implements JavaDelegate {
@Autowired
private PaymentService paymentService;
@Override
public void execute(DelegateExecution execution) {
Order order = (Order) execution.getVariable("order");
try {
// 調(diào)用支付網(wǎng)關(guān)
PaymentResult result = paymentService.processPayment(order);
execution.setVariable("paymentSuccess", result.isSuccess());
execution.setVariable("paymentId", result.getPaymentId());
} catch (Exception e) {
execution.setVariable("paymentSuccess", false);
execution.setVariable("paymentError", e.getMessage());
thrownew RuntimeException("支付處理失敗", e);
}
}
}對(duì)應(yīng)的BPMN流程包含服務(wù)任務(wù):
<process id="orderProcess" name="訂單處理流程">
<startEvent id="start" />
<!-- 庫(kù)存檢查服務(wù)任務(wù) -->
<serviceTask id="inventoryCheck" name="庫(kù)存檢查"
activiti:class="com.example.InventoryCheckDelegate"/>
<!-- 庫(kù)存決策網(wǎng)關(guān) -->
<exclusiveGateway id="inventoryDecision" />
<!-- 有庫(kù)存流程 -->
<sequenceFlow id="inStockFlow" sourceRef="inventoryDecision" targetRef="paymentProcess">
<conditionExpression>${inStock == true}</conditionExpression>
</sequenceFlow>
<!-- 無庫(kù)存流程 -->
<sequenceFlow id="outOfStockFlow" sourceRef="inventoryDecision" targetRef="backorderTask">
<conditionExpression>${inStock == false}</conditionExpression>
</sequenceFlow>
<!-- 支付處理 -->
<serviceTask id="paymentProcess" name="支付處理"
activiti:class="com.example.PaymentProcessDelegate"/>
<!-- 備貨任務(wù) -->
<userTask id="backorderTask" name="備貨處理"
activiti:assignee="#{warehouse}"/>
<endEvent id="end" />
</process>深度原理剖析:
- 服務(wù)任務(wù)異步執(zhí)行:Flowable的
JavaDelegate默認(rèn)支持異步執(zhí)行,避免阻塞主線程 - 執(zhí)行樹優(yōu)化:Flowable將流程執(zhí)行建模為執(zhí)行樹,減少狀態(tài)同步開銷
- 批量事件處理:多個(gè)流程實(shí)例的事件可以批量處理,提升吞吐量
- DMN集成:內(nèi)置DMN決策引擎,支持復(fù)雜業(yè)務(wù)規(guī)則
有些小伙伴在工作中處理高并發(fā)場(chǎng)景時(shí),F(xiàn)lowable的異步執(zhí)行器特別有用。
它能夠?qū)⒑臅r(shí)的服務(wù)任務(wù)放入消息隊(duì)列異步處理,避免數(shù)據(jù)庫(kù)長(zhǎng)事務(wù)。
四、Camunda:微服務(wù)架構(gòu)的首選
Camunda是另一個(gè)流行的Activiti分支,特別強(qiáng)調(diào)運(yùn)維監(jiān)控和微服務(wù)集成能力。
它提供了完整的操作工具鏈。
核心原理
Camunda采用外部任務(wù)模式和樂觀鎖并發(fā)控制。它的核心思想是將工作流引擎與業(yè)務(wù)服務(wù)解耦,通過外部任務(wù)隊(duì)列實(shí)現(xiàn)松耦合集成。
架構(gòu)特色:
- 外部任務(wù)模式,支持多語(yǔ)言客戶端
- 完整的監(jiān)控工具Cockpit和Tasklist
- 基于樂觀鎖的高并發(fā)控制
- 原生支持微服務(wù)架構(gòu)模式
開源地址
GitHub: https://github.com/camunda/camunda-bpm-platform
示例:貸款審批流程(外部任務(wù)模式)
Camunda的外部任務(wù)模式特別適合微服務(wù)架構(gòu):
// 貸款申請(qǐng)DTO
@Data
publicclass LoanApplication {
private String applicationId;
private String applicantName;
privatedouble amount;
privateint duration;
privatedouble income;
privateint creditScore;
}
// 貸款流程啟動(dòng)服務(wù)
@Service
@Transactional
publicclass LoanProcessService {
@Autowired
private RuntimeService runtimeService;
public void startLoanProcess(LoanApplication application) {
Map<String, Object> variables = new HashMap<>();
variables.put("application", application);
variables.put("creditCheckRequired", application.getAmount() > 100000);
variables.put("autoApprovalLimit", 50000.0);
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
"loanApprovalProcess", application.getApplicationId(), variables);
logger.info("貸款流程啟動(dòng): {}", processInstance.getId());
}
}
// 征信檢查外部任務(wù)工作者
@Component
publicclass CreditCheckWorker {
@Autowired
private ExternalTaskService externalTaskService;
@Autowired
private CreditService creditService;
@PostConstruct
public void startCreditCheckWorker() {
new Thread(() -> {
while (true) {
try {
// 獲取外部任務(wù)
List<LockedExternalTask> tasks = externalTaskService
.fetchAndLock(10, "credit-check-worker")
.topic("credit-check", 60000L)
.execute();
for (LockedExternalTask task : tasks) {
processCreditCheck(task);
}
Thread.sleep(5000); // 5秒輪詢
} catch (Exception e) {
logger.error("征信檢查工作者異常", e);
}
}
}).start();
}
private void processCreditCheck(LockedExternalTask task) {
try {
LoanApplication application = (LoanApplication)
task.getVariable("application");
// 調(diào)用外部征信系統(tǒng)
CreditReport report = creditService.getCreditReport(
application.getApplicantName(), application.getApplicantId());
Map<String, Object> variables = new HashMap<>();
variables.put("creditReport", report);
variables.put("creditScore", report.getScore());
variables.put("creditCheckPassed", report.getScore() > 600);
// 完成任務(wù)
externalTaskService.complete(task.getId(), "credit-check-worker", variables);
logger.info("征信檢查完成: {}", application.getApplicationId());
} catch (Exception e) {
// 處理失敗,解鎖任務(wù)以便重試
externalTaskService.handleFailure(task.getId(), "credit-check-worker",
e.getMessage(), 3, 30000L);
}
}
}
// 風(fēng)險(xiǎn)評(píng)估外部任務(wù)工作者
@Component
publicclass RiskAssessmentWorker {
@Autowired
private RiskService riskService;
public void startRiskAssessmentWorker() {
// 類似的實(shí)現(xiàn)模式
// 訂閱"risk-assessment"主題
}
}Camunda還提供強(qiáng)大的運(yùn)維監(jiān)控能力:
// 流程實(shí)例監(jiān)控
@Service
publicclass ProcessMonitorService {
@Autowired
private HistoryService historyService;
@Autowired
private ManagementService managementService;
// 獲取流程實(shí)例統(tǒng)計(jì)
public Map<String, Object> getProcessStats(String processDefinitionKey) {
Map<String, Object> stats = new HashMap<>();
// 運(yùn)行中實(shí)例數(shù)量
long runningInstances = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)
.unfinished()
.count();
// 今日完成數(shù)量
long todayCompleted = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)
.finishedAfter(TemporalUtil.todayStart())
.count();
// 平均處理時(shí)間
HistoricProcessInstanceQuery query = historyService
.createHistoricProcessInstanceQuery()
.processDefinitionKey(processDefinitionKey)
.finished();
stats.put("runningInstances", runningInstances);
stats.put("todayCompleted", todayCompleted);
stats.put("avgDuration", calculateAverageDuration(query));
return stats;
}
}架構(gòu)深度解析:
- 外部任務(wù)模式:業(yè)務(wù)邏輯在引擎外部執(zhí)行,支持多語(yǔ)言和技術(shù)棧
- 主題訂閱機(jī)制:工作者訂閱特定主題的任務(wù),實(shí)現(xiàn)關(guān)注點(diǎn)分離
- 樂觀鎖控制:通過版本號(hào)避免并發(fā)沖突,提高吞吐量
- 完整的運(yùn)維體系:Cockpit提供實(shí)時(shí)監(jiān)控,Optimize提供流程分析
Camunda的這種設(shè)計(jì)特別適合微服務(wù)架構(gòu),每個(gè)微服務(wù)可以獨(dú)立開發(fā)部署,通過外部任務(wù)與工作流引擎交互。
五、jBPM:規(guī)則集成的最佳選擇
jBPM是紅帽旗下的開源BPM套件,基于KIE(Knowledge Is Everything)平臺(tái),與Drools規(guī)則引擎深度集成。
核心原理
jBPM采用知識(shí)會(huì)話模式,將流程引擎和規(guī)則引擎統(tǒng)一在同一個(gè)會(huì)話中。
這種設(shè)計(jì)使得業(yè)務(wù)流程和業(yè)務(wù)規(guī)則能夠無縫協(xié)作。
架構(gòu)特色:
- 與Drools規(guī)則引擎深度集成
- 基于知識(shí)會(huì)話的統(tǒng)一執(zhí)行環(huán)境
- 支持BPMN 2.0和CMN(案例管理)
- 完整的生命周期管理
開源地址
GitHub: https://github.com/kiegroup/jbpm
示例:保險(xiǎn)理賠流程(規(guī)則集成)
jBPM最大的優(yōu)勢(shì)在于規(guī)則和流程的集成:
// 保險(xiǎn)理賠DTO
@Data
publicclass InsuranceClaim {
private String claimId;
private String policyNumber;
private String claimType; // AUTO, HEALTH, PROPERTY
privatedouble claimAmount;
private Date incidentDate;
private String description;
privateboolean fraudulent = false;
privatedouble approvedAmount = 0.0;
}
// 理賠流程服務(wù)
@Service
publicclass ClaimProcessService {
@Autowired
private RuntimeEngine runtimeEngine;
@Autowired
private KieContainer kieContainer;
public void startClaimProcess(InsuranceClaim claim) {
// 創(chuàng)建KIE會(huì)話
KieSession kieSession = kieContainer.newKieSession();
try {
// 插入事實(shí)對(duì)象到規(guī)則引擎
kieSession.insert(claim);
kieSession.insert(new Date());
// 啟動(dòng)流程實(shí)例
ProcessInstance processInstance = runtimeEngine.getKieSession()
.startProcess("claimApprovalProcess",
createProcessVariables(claim));
logger.info("理賠流程啟動(dòng): {}", processInstance.getId());
} finally {
kieSession.dispose();
}
}
private Map<String, Object> createProcessVariables(InsuranceClaim claim) {
Map<String, Object> variables = new HashMap<>();
variables.put("claim", claim);
variables.put("claimAmount", claim.getClaimAmount());
variables.put("claimType", claim.getClaimType());
variables.put("autoApprovalLimit", 5000.0);
variables.put("requiresManagerApproval", claim.getClaimAmount() > 10000);
return variables;
}
}
// 欺詐檢測(cè)規(guī)則(DRL文件)
rule "High Amount Fraud Detection"
when
$claim : InsuranceClaim(claimAmount > 50000)
Date() // 當(dāng)前日期
then
$claim.setFraudulent(true);
modify($claim);
System.out.println("檢測(cè)到大額理賠,標(biāo)記為可疑欺詐: " + $claim.getClaimId());
end
rule "Quick Claim Fraud Detection"
when
$claim : InsuranceClaim(incidentDate after [30d] )
// 事故日期在30天內(nèi)的快速理賠
then
$claim.setFraudulent(true);
modify($claim);
System.out.println("快速理賠標(biāo)記為可疑: " + $claim.getClaimId());
end
rule "Auto Approval for Small Claims"
when
$claim : InsuranceClaim(claimAmount <= 5000, fraudulent == false)
then
$claim.setApprovedAmount($claim.getClaimAmount());
modify($claim);
System.out.println("小額理賠自動(dòng)批準(zhǔn): " + $claim.getClaimId());
end流程定義中集成規(guī)則任務(wù):
<process id="claimApprovalProcess" name="保險(xiǎn)理賠審批流程">
<startEvent id="start" />
<!-- 規(guī)則任務(wù):欺詐檢測(cè) -->
<businessRuleTask id="fraudDetection" name="欺詐檢測(cè)"
g:ruleFlowGroup="fraud-detection" />
<!-- 網(wǎng)關(guān):基于欺詐檢測(cè)結(jié)果路由 -->
<exclusiveGateway id="fraudDecision" />
<!-- 檢測(cè)到欺詐 -->
<sequenceFlow id="fraudDetected" sourceRef="fraudDecision" targetRef="investigateFraud">
<conditionExpression>${claim.fraudulent == true}</conditionExpression>
</sequenceFlow>
<!-- 無欺詐 -->
<sequenceFlow id="noFraud" sourceRef="fraudDecision" targetRef="approvalDecision">
<conditionExpression>${claim.fraudulent == false}</conditionExpression>
</sequenceFlow>
<!-- 規(guī)則任務(wù):自動(dòng)理賠決策 -->
<businessRuleTask id="approvalDecision" name="理賠決策"
g:ruleFlowGroup="claim-approval" />
<!-- 欺詐調(diào)查人工任務(wù) -->
<userTask id="investigateFraud" name="欺詐調(diào)查"
g:assignee="fraud_investigator" />
<endEvent id="end" />
</process>深度集成原理:
- 統(tǒng)一知識(shí)會(huì)話:流程實(shí)例和規(guī)則引擎共享同一個(gè)KIE會(huì)話
- 事實(shí)對(duì)象同步:流程變量自動(dòng)作為事實(shí)插入規(guī)則引擎
- 規(guī)則流組:通過規(guī)則流組控制規(guī)則執(zhí)行順序
- 動(dòng)態(tài)規(guī)則更新:支持運(yùn)行時(shí)更新業(yè)務(wù)規(guī)則而不影響流程實(shí)例
有些小伙伴在處理復(fù)雜業(yè)務(wù)規(guī)則時(shí),jBPM的這種集成模式特別有價(jià)值。
比如在風(fēng)控場(chǎng)景中,反欺詐規(guī)則經(jīng)常變化,使用jBPM可以獨(dú)立更新規(guī)則而不需要修改流程定義。
六、JDEasyFlow:輕量級(jí)流程編排新星
JDEasyFlow是京東自研的輕量級(jí)流程編排組件,特點(diǎn)是簡(jiǎn)單、靈活、易擴(kuò)展。
它適用于不需要完整BPMN功能的簡(jiǎn)單場(chǎng)景。
核心原理
JDEasyFlow采用基于JSON的流程定義和狀態(tài)機(jī)引擎。
它的設(shè)計(jì)理念是"約定優(yōu)于配置",通過簡(jiǎn)單的節(jié)點(diǎn)定義實(shí)現(xiàn)靈活的流程編排。
架構(gòu)特色:
- 基于JSON的聲明式流程定義
- 無數(shù)據(jù)庫(kù)依賴,純內(nèi)存執(zhí)行
- 支持BPMN元素子集
- 極低的學(xué)習(xí)成本
開源地址
GitHub: https://github.com/JDEasyFlow/jd-easyflow
示例:簡(jiǎn)單訂單狀態(tài)流轉(zhuǎn)
JDEasyFlow特別適合簡(jiǎn)單的狀態(tài)機(jī)場(chǎng)景:
首先引入依賴:
<dependency>
<groupId>com.jd.easyflow</groupId>
<artifactId>easyflow-flow</artifactId>
<version>1.2.0</version>
</dependency>定義訂單狀態(tài)流程JSON:
{
"id": "order_flow",
"name": "訂單狀態(tài)流程",
"nodes": [
{
"id": "created",
"name": "訂單創(chuàng)建",
"start": true,
"post": {
"to": "paid"
}
},
{
"id": "paid",
"name": "已支付",
"action": {
"createExp": "new com.example.order.PaymentAction()"
},
"post": {
"to": "shipped"
}
},
{
"id": "shipped",
"name": "已發(fā)貨",
"action": {
"createExp": "new com.example.order.ShipmentAction()"
},
"post": {
"to": "received"
}
},
{
"id": "received",
"name": "已收貨",
"action": {
"createExp": "new com.example.order.ReceiveAction()"
},
"post": {
"to": "completed"
}
},
{
"id": "completed",
"name": "已完成"
},
{
"id": "cancelled",
"name": "已取消"
}
]
}Java節(jié)點(diǎn)動(dòng)作實(shí)現(xiàn):
// 支付節(jié)點(diǎn)動(dòng)作
@Component
publicclass PaymentAction implements FlowNodeAction {
@Autowired
private PaymentService paymentService;
@Override
public Object execute(FlowRequest request, FlowContext context) {
String orderId = (String) request.getParam("orderId");
// 執(zhí)行支付邏輯
PaymentResult result = paymentService.confirmPayment(orderId);
// 設(shè)置節(jié)點(diǎn)輸出
Map<String, Object> resultData = new HashMap<>();
resultData.put("paymentId", result.getPaymentId());
resultData.put("paymentTime", result.getPaymentTime());
resultData.put("success", result.isSuccess());
logger.info("訂單{}支付處理完成", orderId);
return resultData;
}
}
// 發(fā)貨節(jié)點(diǎn)動(dòng)作
@Component
publicclass ShipmentAction implements FlowNodeAction {
@Autowired
private LogisticsService logisticsService;
@Override
public Object execute(FlowRequest request, FlowContext context) {
String orderId = (String) request.getParam("orderId");
// 調(diào)用物流服務(wù)發(fā)貨
ShipmentInfo shipment = logisticsService.createShipment(orderId);
Map<String, Object> resultData = new HashMap<>();
resultData.put("shipmentId", shipment.getShipmentId());
resultData.put("shipmentTime", shipment.getShipmentTime());
resultData.put("logisticsCompany", shipment.getCompany());
logger.info("訂單{}發(fā)貨完成,物流單號(hào): {}", orderId, shipment.getShipmentId());
return resultData;
}
}流程引擎配置和使用:
@Configuration
publicclass EasyFlowConfig {
@Bean
public FlowEngine flowEngine() {
FlowEngineImpl flowEngine = new FlowEngineImpl();
flowEngine.setFlowPath("classpath:flows/order_flow.json");
flowEngine.init();
return flowEngine;
}
}
@Service
publicclass OrderFlowService {
@Autowired
private FlowEngine flowEngine;
// 執(zhí)行訂單流程
public void processOrder(String orderId, String action) {
Map<String, Object> params = new HashMap<>();
params.put("orderId", orderId);
params.put("action", action);
FlowParam flowParam = new FlowParam("order_flow", params);
// 執(zhí)行流程
FlowResult result = flowEngine.execute(flowParam);
if (result.isSuccess()) {
logger.info("訂單流程執(zhí)行成功: {}", result.getNodeIds());
} else {
logger.error("訂單流程執(zhí)行失敗: {}", result.getMessage());
thrownew RuntimeException("流程執(zhí)行失敗: " + result.getMessage());
}
}
// 獲取下一個(gè)可用節(jié)點(diǎn)
public List<String> getNextNodes(String orderId, String currentNode) {
Map<String, Object> params = new HashMap<>();
params.put("orderId", orderId);
FlowParam flowParam = new FlowParam("order_flow", params);
// 解析流程定義,獲取后續(xù)節(jié)點(diǎn)
return flowEngine.getNextNodes(flowParam, currentNode);
}
}輕量級(jí)架構(gòu)解析:
- JSON定義:通過簡(jiǎn)單的JSON結(jié)構(gòu)定義節(jié)點(diǎn)和流轉(zhuǎn)
- 無持久化:純內(nèi)存執(zhí)行,性能極高
- 插件化動(dòng)作:通過接口實(shí)現(xiàn)業(yè)務(wù)邏輯
- 條件支持:支持簡(jiǎn)單的條件表達(dá)式路由
JDEasyFlow的這種設(shè)計(jì)特別適合服務(wù)編排和簡(jiǎn)單狀態(tài)機(jī)場(chǎng)景。
有些小伙伴在微服務(wù)架構(gòu)中需要編排多個(gè)服務(wù)調(diào)用,但又不想引入沉重的BPMN引擎,JDEasyFlow是完美選擇。
七、五種工作流引擎全面對(duì)比
為了幫助大家更好地進(jìn)行技術(shù)選型,我整理了這五種工作流引擎的詳細(xì)對(duì)比:
特性維度 | Activiti | Flowable | Camunda | jBPM | JDEasyFlow |
學(xué)習(xí)曲線 | 中等 | 中等 | 較陡 | 較陡 | 簡(jiǎn)單 |
性能 | 良好 | 優(yōu)秀 | 優(yōu)秀 | 良好 | 極佳 |
功能完備性 | 高 | 高 | 很高 | 高 | 中等 |
監(jiān)控運(yùn)維 | 基礎(chǔ) | 良好 | 優(yōu)秀 | 良好 | 簡(jiǎn)單 |
社區(qū)生態(tài) | 活躍 | 活躍 | 活躍 | 活躍 | 一般 |
云原生支持 | 基礎(chǔ) | 良好 | 優(yōu)秀 | 良好 | 良好 |
規(guī)則集成 | 基礎(chǔ) | 基礎(chǔ) | 良好 | 優(yōu)秀 | 無 |
適用場(chǎng)景 | 傳統(tǒng)企業(yè)應(yīng)用 | 高性能業(yè)務(wù) | 微服務(wù)架構(gòu) | 規(guī)則密集型 | 輕量級(jí)編排 |
為了更直觀地展示選型邏輯,我畫了一個(gè)決策流程圖:
圖片
八、總結(jié)
下面我們來總結(jié)一下工作流引擎選型的一些建議:
- 傳統(tǒng)企業(yè)級(jí)應(yīng)用:選擇Activiti,生態(tài)成熟,文檔豐富,遇到問題容易找到解決方案
- 高性能高并發(fā)場(chǎng)景:選擇Flowable,它在Activiti基礎(chǔ)上做了大量性能優(yōu)化
- 微服務(wù)架構(gòu):選擇Camunda,外部任務(wù)模式完美契合微服務(wù)理念,運(yùn)維監(jiān)控工具完善
- 規(guī)則密集型業(yè)務(wù):選擇jBPM,與Drools規(guī)則引擎深度集成,適合風(fēng)控、保險(xiǎn)等場(chǎng)景
- 輕量級(jí)服務(wù)編排:選擇JDEasyFlow,簡(jiǎn)單靈活,學(xué)習(xí)成本低,無外部依賴
有些小伙伴在工作中可能會(huì)想:"能不能一個(gè)項(xiàng)目用多種工作流引擎?"
我的經(jīng)驗(yàn)是:可以,但要明確邊界。
比如用Camunda管理核心業(yè)務(wù)流程,用JDEasyFlow做微服務(wù)內(nèi)部編排。
最后記?。?/span>沒有最好的工作流引擎,只有最合適的。
選型時(shí)要綜合考慮團(tuán)隊(duì)技術(shù)棧、業(yè)務(wù)場(chǎng)景、性能要求和運(yùn)維能力。
































