原文鏈接:https://blog.csdn.net/hellozpc/article/details/80293387
規則引擎
相關介紹
規則引擎起源於基於規則的專家系統,而基於規則的專家系統又是專家系統的其中一個分支。專家系統又屬於人工智能的一個研究分支,它模仿人類的推理方式,使用試探性的方法進行推理,並使用人類能理解的術語解釋和證明它的推理結論。
利用規則引擎可以在業務系統中分離商業決策者的商業決策邏輯和應用開發者的技術決策,並把這些商業決策放在中心數據庫或其他統一的地方,讓它們能在運行時可以動態地管理和修改,從而爲企業保持靈活性和競爭力提供有效的技術支持。
在需求裏面我們往往把約束,完整性,校驗,分支流等都可以算到業務規則裏面。在規則引擎裏面談的業務規則重點是談當滿足什麼樣的條件的時候,需要執行什麼樣的操作。因此一個完整的業務規則包括了條件和觸發操作兩部分內容。而引擎是事物內部的重要的運行機制,規則引擎即重點是解決規則如何描述,如何執行,如何監控等一系列問題。
規則引擎由推理引擎發展而來,是一種嵌入在應用程序中的組件,實現了將業務決策從應用程序代碼中分離出來,並使用預定義的語義模塊編寫業務決策。接受數據輸入,解釋業務規則,並根據業務規則做出業務決策。
java開源的規則引擎有:Drools、Easy Rules、Mandarax、IBM ILOG。使用最爲廣泛並且開源的是Drools。
規則引擎的優點
聲明式編程
規則可以很容易地解決困難的問題,並得到解決方案的驗證。與代碼不同,規則以較不復雜的語言編寫; 業務分析師可以輕鬆閱讀和驗證一套規則。
邏輯和數據分離
數據位於“域對象”中,業務邏輯位於“規則”中。根據項目的種類,這種分離是非常有利的。
速度和可擴展性
寫入Drools的Rete OO算法已經是一個成熟的算法。在Drools的幫助下,您的應用程序變得非常可擴展。如果頻繁更改請求,可以添加新規則,而無需修改現有規則。
知識集中化
通過使用規則,您創建一個可執行的知識庫。這是商業政策的一個真理點。理想情況下,規則是可讀的,它們也可以用作文檔。
rete 算法
Rete 算法最初是由卡內基梅隆大學的 Charles L.Forgy 博士在 1974 年發表的論文中所闡述的算法 , 該算法提供了專家系統的一個高效實現。自 Rete 算法提出以後 , 它就被用到一些大型的規則系統中 , 像 ILog、Jess、JBoss Rules 等都是基於 RETE 算法的規則引擎
Rete 在拉丁語中譯爲”net”,即網絡。Rete 匹配算法是一種進行大量模式集合和大量對象集合間比較的高效方法,通過網絡篩選的方法找出所有匹配各個模式的對象和規則。
其核心思想是將分離的匹配項根據內容動態構造匹配樹,以達到顯著降低計算量的效果。Rete 算法可以被分爲兩個部分:規則編譯和規則執行。當Rete算法進行事實的斷言時,包含三個階段:匹配、選擇和執行,稱做 match-select-act cycle。
Drools 介紹
Drools 是一個基於Charles Forgy’s的RETE算法的,易於訪問企業策略、易於調整以及易於管理的開源業務規則引擎,符合業內標準,速度快、效率高。業務分析師人員或審覈人員可以利用它輕鬆查看業務規則,從而檢驗是否已編碼的規則執行了所需的業務規則。
Drools 是用Java語言編寫的開放源碼規則引擎,使用Rete算法對所編寫的規則求值。Drools允許使用聲明方式表達業務邏輯。可以使用非XML的本地語言編寫規則,從而便於學習和理解。並且,還可以將Java代碼直接嵌入到規則文件中,這令Drools的學習更加吸引人。
Drools優點:
非常活躍的社區支持
易用
快速的執行速度
在 Java 開發人員中流行
與 Java Rule Engine API(JSR 94)兼容
Drools相關概念:
事實(Fact):對象之間及對象屬性之間的關係
規則(rule):是由條件和結論構成的推理語句,一般表示爲if…Then。一個規則的if部分稱爲LHS,then部分稱爲RHS。
模式(module):就是指IF語句的條件。這裏IF條件可能是有幾個更小的條件組成的大條件。模式就是指的不能在繼續分割下去的最小的原子條件。
Drools通過事實、規則和模式相互組合來完成工作,Drools在開源規則引擎中使用率最廣,但是在國內企業使用偏少,保險、支付行業使用稍多。
Example
需求背景:公司雙十一大促,活動規則是根據用戶購買訂單的金額給用戶送相應的積分,購買的越多送的積分越多,用戶可以使用積分來兌換相應的商品。
用戶購買的金額和對應送多少積分的規則如下:
100元以下, 不加分
100元-500元 加100分
500元-1000元 加500分
1000元 以上 加1000分
注意:這樣的業務規則會根據實際銷售情況變化,比如買的人少了,可以加大積分額度。買的人好多可以適當地降低積分贈送~因此規則引擎派上了用場。
導入maven依賴
<properties>
<drools.version>7.7.0.Final</drools.version>
</properties>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>${drools.version}</version>
</dependency>
配置xxx.drl規則文件和kmodule.xml文件
規則文件 resources\rules\Point-rules.drl
package rules
import com.zpc.boot.Order
rule "zero"
no-loop true
lock-on-active true
salience 1
when
$s : Order(amout <= 100)
then
$s.setScore(0);
update($s);
end
rule "add100"
no-loop true
lock-on-active true
salience 1
when
$s : Order(amout > 100 && amout <= 500)
then
$s.setScore(100);
update($s);
end
rule "add500"
no-loop true
lock-on-active true
salience 1
when
$s : Order(amout > 500 && amout <= 1000)
then
$s.setScore(500);
update($s);
end
rule "add1000"
no-loop true
lock-on-active true
salience 1
when
$s : Order(amout > 1000)
then
$s.setScore(1000);
update($s);
end
package 與Java語言類似,drl的頭部需要有package和import的聲明,package不必和物理路徑一致。
import 導出java Bean的完整路徑,也可以將Java靜態方法導入調用。
rule 規則名稱,需要保持唯一件,可以無限次執行。
no-loop 定義當前的規則是否不允許多次循環執行,默認是 false,也就是當前的規則只要滿足條件,可以無限次執行。
lock-on-active 將lock-on-active屬性的值設置爲true,可避免因某些Fact對象被修改而使已經執行過的規則再次被激活執行。
salience 用來設置規則執行的優先級,salience 屬性的值是一個數字,數字越大執行優先級越高, 同時它的值可以是一個負數。默認情況下,規則的 salience 默認值爲 0。如果不設置規則的 salience 屬性,那麼執行順序是隨機的。
when 條件語句,就是當到達什麼條件的時候
then 根據條件的結果,來執行什麼動作
end 規則結束
這個規則文件就是描述了,當符合什麼條件的時候,應該去做什麼事情,每當規則有變動的時候,我們只需要修改規則文件,然後重新加載即可生效。
這裏需要有一個配置文件告訴代碼規則文件drl在哪裏,在Drools中這個文件就是kmodule.xml,放置到resources/META-INF目錄下。
配置文件 resources/META-INF/kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/kie/6.0.0/kmodule ">
<kbase name="point-rulesKB" packages="rules">
<ksession name="point-rulesKS"/>
</kbase>
</kmodule>
Kmodule 中可以包含一個到多個 kbase,分別對應 drl 的規則文件。
Kbase 需要一個唯一的 name,可以取任意字符串。
packages爲drl文件所在resource目錄下的路徑。注意區分drl文件中的package與此處的package不一定相同,多個包用逗號分隔。默認情況下會掃描 resources目錄下所有(包含子目錄)規則文件。
kbase的default屬性,標示當前KieBase是不是默認的,如果是默認的則不用名稱就可以查找到該 KieBase,但每個 module 最多只能有一個默認 KieBase。
kbase 下面可以有一個或多個 ksession,ksession 的 name 屬性必須設置,且必須唯一。
編寫後端代碼
Order.java
public class Order {
private String orderNum;
private int amout;
private int score;
public Order(String orderNum,int amout) {
this.orderNum = orderNum;
this.amout=amout;
}
public String getOrderNum() {
return orderNum;
}
public void setOrderNum(String orderNum) {
this.orderNum = orderNum;
}
public int getAmout() {
return amout;
}
public void setAmout(int amout) {
this.amout = amout;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Order{" +
"orderNum='" + orderNum + '\'' +
", amout=" + amout +
", score=" + score +
'}';
}
}
DroolMain .java
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import java.util.ArrayList;
import java.util.List;
public class DroolMain {
public static final void main(final String[] args) throws Exception {
KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
List<Order> result = execute(kc);
printResult(result);
}
private static void printResult(List<Order> results) {
for (Order order : results) {
System.out.println(order);
}
}
public static List<Order> execute(KieContainer kc) throws Exception {
KieSession ksession = kc.newKieSession("point-rulesKS");
List<Order> orderList = getOrders();
for (int i = 0; i < orderList.size(); i++) {
Order o = orderList.get(i);
ksession.insert(o);
ksession.fireAllRules();
}
ksession.dispose();
return orderList;
}
private static List<Order> getOrders() {
List<Order> orderList = new ArrayList<Order>();
orderList.add(new Order("xxx00001", 1200));
orderList.add(new Order("xxx00002", 200));
orderList.add(new Order("xxx00003", 99));
orderList.add(new Order("xxx00004", 499));
orderList.add(new Order("xxx00005", 999));
return orderList;
}
}
運行代碼
Order{orderNum=’xxx00001’, amout=1200, score=1000}
Order{orderNum=’xxx00002’, amout=200, score=100}
Order{orderNum=’xxx00003’, amout=99, score=0}
Order{orderNum=’xxx00004’, amout=499, score=100}
Order{orderNum=’xxx00005’, amout=999, score=500}
看到所有的訂單被規則引擎處理後有了相應的score值~
業務需求變更後只要修改對應的配置文件即可實現新的積分贈送策略!