規則引擎深度對比,LiteFlow vs Drools!
大家好,我是不才陳某~
Drools 是一款老牌的 java 規則引擎框架,早在十幾年前,我剛工作的時候,曾在一家第三方支付企業工作。在覈心的支付路由層面我記得就是用 Drools 來做的。
難能可貴的是,Drools 這個項目在十幾年後還依舊保持着開源和更新。
https://github.com/kiegroup/drools
而 LiteFlow 也是一款 java 規則引擎,於 2020 年開源。經過 2 年的迭代,現在功能和特性也非常棒,很適合用在高複雜度的核心業務上,同時又能保持業務的靈活性。
https://gitee.com/dromara/liteFlow
這篇文章我們就來深入比較下這兩款框架,都適合用在什麼樣的場景,有什麼異同點,以及在相同的場景下表現力如何。
(其中 Drools 基於 7.6.0 版本,LiteFlow 基於 2.9.0 版本)
雖然題主就是開源項目 LiteFlow 的作者,但是我這幾天也深入瞭解了下 Drools,儘量從很客觀的角度嘗試去分析。很多比對的結果都是基於實際使用後的感受。不過題主難免會帶有一些主觀的心理以及瞭解的片面性,尤其是 Drools 現在已經更新到了 8.X。
規則引擎的定義
首先我想明確下規則引擎的定義,因爲很多小夥伴容易把規則引擎和流程引擎的概念混在一起。
規則引擎通常是嵌入在應用程序組件中的,實現了將業務決策從應用程序代碼中分離出來,並使用預定義的語義模塊編寫業務決策。接受數據輸入,解釋業務規則,並根據業務規則做出業務決策。
簡單來說就是,規則引擎主要解決易變邏輯和業務耦合的問題,規則驅動邏輯。以前項目內寫死在代碼裏的邏輯用規則引擎可以提出來,隨時熱變更。
而流程引擎實現了將多個業務參與者之間按照某種預定義的規則進行流轉,通常需要涉及到角色信息。
簡單來說就是,流程引擎主要解決業務在不同角色之間的流轉問題,如請假流程,審批流程,往往要經過多個角色。規則驅動角色流轉。
兩款框架的異同點
Drools 和 LiteFlow 都是優秀的開源框架,都能把業務中的邏輯給剝離出來。並且擁有自己表達式語法。
但是有所區別的是,Drools 強調邏輯的片段規則化,你可以把核心易變部分寫成一個規則文件,等同於原先寫在 java 裏的代碼現在搬遷到了規則文件。規則文件裏的代碼全都是可以熱變更的。
而 LiteFlow 是基於組件式的思想設計的,更強調組件的規則化,覆蓋範圍是整個業務。編排的最小單位是組件,規則文件用來串聯組件間的流轉。同時 LiteFlow 也支持片段式的代碼規則化,因爲 LiteFlow 也支持業務邏輯的腳本化。規則支持熱變更。
所以評判一個規則引擎是否合格的主要因素有:
-
有沒有靈活的規則表達式來支持
-
規則和 Java 之間能否非常方便的聯動
-
API 調用是否方便,和各種場景系統的集成如何
-
侵入性耦合比較
-
規則的學習成本,是否容易上手
-
規則表達式是否有語言插件
-
規則能否和業務松耦合,存儲於其他地方
-
規則的變更能否實時改變邏輯
-
是否有界面形態來支持非技術人員的使用
-
框架的性能表現
下面就從這幾個方面來細細比較兩款框架的表現力
規則表達式
Drools 的規則表達式爲 Java 量身定製的基於 Charles Forgy 的 RETE 算法的規則引擎的實現。
Drools 的規則表達式貼近自然編程語言,擁有自己的擴展名文件 drl,語法支持全,基本上自然編程語言有的語法 drl 全有。所以,完全可以把 java 的邏輯寫在 drl 文件中。
來看下 drl 文件的大體樣子:
drl 文件
可以看到,Drools 定義規則的方式是一個規則一段,有明確的 when...then,表示當滿足什麼條件時,做什麼。關注公 z 號:碼猿技術專欄,回覆關鍵詞:1111 獲取阿里內部 Java 性能優化手冊!在觸發規則時候,會自動判斷該去執行哪一段 rule,如果滿足多個條件,是可以觸發多個規則的 then 的。
LiteFlow 編排表達式簡單易懂,底層用 EL 表達式語言包裝而成。用於組件的流轉,支持異步,選擇,條件,循環,嵌套等一些場景。
組件層面不僅可以是 java 組件,還可以用腳本語言來編寫,目前支持了 Groovy 和 QLExpress 兩種腳本語言。所有能用 java 實現的,用腳本語言都可以做到。
LiteFlow 的規則文件大體長這個樣子:
LiteFlow 的規則文件
上述 LiteFlow 的編排表達式中,所表達的是下面一個邏輯流:
LiteFlow 的編排表達式
LiteFlow 編排表達式支持 THEN(同步),WHEN(異步),SWITCH(選擇),IF(條件),FOR(次數循環),WHILE(條件循環) 等大表達式,每個表達式又有許多擴展關鍵字可供選用。
腳本組件支持的 Groovy 基本和 java 語法差不多,Groovy 語言支持的一切你均可使用。甚至可以在 Groovy 語法中額外定義類和方法。
「結論」
總的來說,兩款框架都能用腳本來定義邏輯片段,在定義邏輯片段層面,Drools 使用的是自研語法,LiteFlow 使用的是插件式的 Groovy,其實個人覺得 Groovy 更接近 java 語法,你甚至於可以在其中定義類和方法。Drools 在高級應用中,也可以用規則定義方法,但是我覺得並不那麼自然。
LiteFlow 最大的特點是除了定義邏輯片段外,還可以進行全局組件的編排。而這正是 LiteFlow 稱之爲編排式規則引擎的由來。使用簡單的編排語法可以設計出複雜的邏輯流。支持 java 和腳本混編。
和 Java 的數據交換
在 Drools 的規則中,你可以通過import
關鍵字來引入 java 的一些類包類進行調用。
在 LiteFlow 的腳本組件中,Groovy 也可以通過import
來引入 java 的任何包來調用。
Drools 中,可以直接引用到 fact 對象。
LiteFlow 中,可以直接引用到 context 對象,context 上下文貫穿整個編排鏈路。
LiteFlow 中,通過 @ScriptBean 註解,你甚至可以把 spring 上下文中的 bean 引入進來直接調用。利用這個特性,甚至於可以在腳本中調用 rpc,調用數據庫 dao 對象取數據。這個在 Drools 裏面雖然也可以做到,但是要麻煩的多。
「結論」
基本都能滿足和 java 的數據交換需求,但是 LiteFlow 在場景上支持的顯然更加多一點。
API 以及集成
在 API 調用層面,Drools 需要去定義 KieContainer,KBase,KSession 一系列對象。LiteFlow 框架只需要使用到 LiteFlowExecutor 對象。
Drools 支持了編程式接入,但是在 springboot 中需要自己寫很多配置類來去集成。
LiteFlow 不僅支持了編程式接入,在 springboot 環境下更是提供了自動裝配的 starer 接入方式,連定義 LiteFlowExecutor 都不需要,直接從上下文中就可以拿到自動裝配後的對象進行調用。
結論
LiteFlow api 更加簡單,同 Springboot 集成度更加高。
侵入性耦合比較
Drools 需要在 java 代碼裏需要用到規則的地方用 KSession 對象去匹配規則進行調用。規則和 java 是分離的。在調用層面耦合了 KSession 調用對象。
LiteFlow 的規則和 java 也是分離的,但是 LiteFlow 多了組件這一概念,所以在組件層面是需要繼承的,但是同時也提供聲明式組件的選擇,使用聲明式的方式耦合相對要減少一些。在調用層面也需要去調用 LiteFlowExecutor 對象。
「結論」
在耦合度上面,由於 LiteFlow 提供編排特性,API 耦合度相對稍高一些。Drools 耦合少一些。
規則的學習成本
Drools 的規則學習成本挺高的。由於是自研的規則語法,需要一個很全面的熟悉過程。而且文檔全英文。
LiteFlow 的編排規則極其簡單,如果你不使用腳本組件的話,基本上 10 分鐘即可上手。就算使用了 groovy 腳本,由於 groovy 非常類似於 java,學習成本也非常少。況且有大量的學習資料可以參閱。
LiteFlow 的文檔中英文齊全,還有良好的中文社區可以答疑解惑。
結論
在規則學習成本上,Drools 的規則學習曲線比 LiteFlow 高出不止一丁點。
是否有語言插件
Drools 在 Eclipse 和 IDEA 上均有插件來做語法的高亮,預檢查和提示。
LiteFlow 在 IDEA 上有插件來做高亮,預檢查和提示。Eclipse 上沒有。
結論
考慮到使用 eclipse 的人幾乎很少了,基本上 2 款規則引擎在語言插件上都做到了。
規則的存儲
Drools 的規則理論上支持你的規則存於任何地方,但這一切都需要你手動去額外完成。自己去存,自己去取。
Drools 還有款 workbeanch 的插件,可以將規則存於 workbeanch 中。只有這個是不需要自己存取的。
LiteFlow 除了本地規則以外,原生支持將規則存儲於任何標準 SQL 的數據庫,還原生支持了 Nacos,Etcd,zookeeper 等註冊中心。只需要配置一下即可。除此之外,還提供了擴展接口,方便你自己擴展成任意的存儲點。
「結論」
LiteFlow 的規則存儲支持比 Drools 豐富的多。
規則的變更能否實時改變邏輯
Drools 熱刷新規則的方式現在看起來有點傻,它的規則是通過生成 jar 的方式。然後系統遠程動態讀取 jar 包來完成規則刷新的。
而且一定得通過 workbench 的方式進行規則的熱變更。
LiteFlow 在這個層面做的高級很多。如果你是用 Nacos,Etcd,zookeeper 等方式存儲,不用做任何事,改變即自動刷新。如果你是 SQL 數據庫存儲,或者本地存儲。在改變規則之後,需要調用 LiteFlow 框架提供的一個 API 進行熱變更。2 種方式均可熱更新。並且在高併發情況下是平滑的。
「結論」
LiteFlow 在熱更新設計層面比 Drools 先進很多。
是否有界面形態來支持
Drools 有 workbench,workbench 是一個獨立的插件包,提供了 web 界面編寫規則以及 fact 對象。並提供了檢查和部署的能力。但因爲 Drools 主要關心邏輯片段,並不需要提供編排層面的拖拽 UI 功能,只是提供了在界面上編寫規則的能力。
LiteFlow 並沒有界面形態。目前只能通過第三方的 Nacos,Etcd 提供的界面來輔助完成界面的規則修改。
「結論」
Drools 在 UI 形態生態上領先 LiteFlow 一截。
框架的性能表現
這裏用 Drools 和 LiteFlow 實現了同樣的一段邏輯 Demo。
根據訂單金額來加積分的 Demo 案例。
案例邏輯很簡單,根據訂單的金額來動態判斷該加多少積分:
小於 100 元,不加積分。
100 到 500 元,加 100 積分。
500 到 1000 元,加 500 積分。
1000 元以上,加 1000 積分。
其中 Drools 的規則如下:
package rules;
import com.example.droolsdemo.entity.Order;
rule "score_1"
when
$order:Order(amount<100)
then
$order.setScore(0);
System.out.println("觸發了規則1");
end
rule "score_2"
when
$order:Order(amount>=100 && amount < 500)
then
$order.setScore(100);
System.out.println("觸發了規則2");
end
rule "score_3"
when
$order:Order(amount>=500 && amount < 1000)
then
$order.setScore(500);
System.out.println("觸發了規則3");
end
rule "score_4"
when
$order:Order(amount>=1000)
then
$order.setScore(1000);
System.out.println("觸發了規則4");
end
其中等價的 LiteFlow 規則如下:
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="w" type="switch_script">
<![CDATA[
def amount = defaultContext.getData("order").getAmount();
if (amount < 100){
return "a";
}else if(amount >= 100 && amount < 500){
return "b";
}else if(amount >= 500 && amount < 1000){
return "c";
}else{
return "d";
}
]]>
</node>
<node id="a" type="script">
<![CDATA[
def order = defaultContext.getData("order");
order.setScore(0);
println("執行規則a");
]]>
</node>
<node id="b" type="script">
<![CDATA[
def order = defaultContext.getData("order");
order.setScore(100);
println("執行規則b");
]]>
</node>
<node id="c" type="script">
<![CDATA[
def order = defaultContext.getData("order");
order.setScore(500);
println("執行規則c");
]]>
</node>
<node id="d" type="script">
<![CDATA[
def order = defaultContext.getData("order");
order.setScore(1000);
println("執行規則d");
]]>
</node>
</nodes>
<chain >
SWITCH(w).TO(a, b, c, d);
</chain>
</flow>
兩款框架都全用腳本來寫的情況下,測試的過程中,去除所有的打印日誌,執行 10w 次,得到的結果如下:
Drools 執行 10w 次,耗時 0.7 秒
LiteFlow 全腳本組件執行 10w 次,耗時 3.6 秒
由於 LiteFlow 在全腳本組件的情況下,需要做腳本的執行和編排腳本的執行,所以花費的時間更長。
如果 LiteFlow 把組件更換成 java,再進行執行,得到的結果如下:
LiteFlow 全 Java 組件執行 10w 次,耗時 0.5 秒
結論
如果 LiteFlow 採用全腳本的方式運行,耗時會比 Drools 更長。如果採用全 java 組件的方式運行,其性能能超越 Drools 一點。
所以對於 LiteFlow 而言,如果你希望更高的性能,則採用 java 組件,如果你希望更高的靈活性,則採用腳本組件。
其實在實際業務中,把容易更改的邏輯抽出來寫成腳本組件,採用 java + 腳本混編的方式,是更爲推薦的做法。
結語
爲什麼會拿 Drools 來作爲比較,其一在題主心中,Drools 一直是規則引擎界的標杆,drools 有很多理念非常值得學習。其二也是因爲題主也只熟悉 Drools,其他的框架沒有很好的使用過的緣故。
但是綜合來看,作爲國產規則引擎後起之秀 LiteFlow 顯然在設計理念,支持度方面是要優於 Drools 的。編排式規則引擎作爲規則引擎的一個新的方向,也會一直探索下去的。希望大家能多多支持這款國產的規則引擎。在編排方向,LiteFlow 除了文中所提到的一些特性以外,還有很多其他各種各樣的探索性的玩法和高級特性。是一款很值得深挖的框架。
來源:https://juejin.cn/post/7155672111481094152
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/5NPcNq_gPuH63ZN5orjF8Q