Drools入門簡介

規則引擎的核心目的之一是將業務決策從程序代碼中分離出來,使其代碼與業務解耦合。通過特定的語法內容編寫業務模塊,由API進行解析並對外提供執行接口,再接收輸入數據、進行業務邏輯處理並返回執行結果。

自6.0版本開始,基於KIE(Knowledge is Everything)的全新API,其目的是為了更簡單地操作規則引擎。


KIE的生命週期

1. 創建:通過KIE-WB創建知識庫,如DRL、決策表、決策樹、實體等;

2. 構建:構建一個可提供KIE部署的組件,簡單地說,就是生成一個包含知識庫的jar包,通過Java代碼或KIE-WB提供的服務器(KIE-Server)來操作業務規則;

3. 測試:在構建部署前,對整體知識庫進行測試;

4. 部署:KIE使用Maven將其組件部署到應用程序上;

5. 使用:通過KieContainer創建Kie會話(KieSession),為執行提供前提條件;

6. 執行:通過執行KieSession的交互,通過代碼或頁面進行操作。

7. 交互:用戶與KieSession的交互,通過代碼或頁面進行操作。

8. 管理:管理KieSession、KieContainer等Drools提供的相關對象。

為什麼要用規則引擎?

1. 給運營人員帶來什麼?

2. 給IT帶來什麼?

*業務規則與系統代碼分離,實現代碼和業務的解耦合。*

提供領域語言,使業務人員更容易理解。

應用場景:

行業:金融、醫療、電商

系統分類:風控、決策平臺、促銷平臺

經典的代碼結構

規則文件內容一般包括三大塊,即包路徑(package,永遠在第一行)、引用(import)、規則體(rule)。

規則文件接口rule.drl

<code>```
package rules.rulesHelo
\trule “規則1"
\t\twhen
\t\t\teval(true);
\t\tthen
\t\t\tSystem.out.println(“”hello world);
\t\tend
```/<code>

執行代碼段:

<code>```
KieServices kss = KieServices.Factory.*get*();
KieContainer kc = kss.getKieClasspathContainer();

KieSession ks = kc.newKieSession(“ksession name”);
int i = ks.fireAllRules();
```/<code>

kmodule.xml配置文件

```

<code><kmodule>
\t<kbase>規則體

規則體是規則文件內容中的核心,分為LHS(when與then之間)、RHS(then與end之間)兩大功能模塊。

LHS:條件部分,又稱為Left Hand Side,條件部分結果總是返回true;

RHS:結果部分,又稱為Right Hand Side;

這裡要注意的是,在*Rete算法*中,規則在匹配時只會執行LHS為true的規則,加載規則時,會將所有規則體中的LHS部分先執行,即規則庫中的LHS部分會被先一步加載。

Fact

只有Fact對象發生了改變,規則體才有可能重新被激活,之前為false的LHS就有可能變為true。

Drools規則引擎中傳遞的數據,術語稱為Fact對象(比如一個JavaBean,Fact對象不是對一個JavaBean對象進行克隆,而是原來JavaBean對象的**引用**),規則體中可以對當前對象進行任何的讀/寫操作,調用該對象提供的方法。

引用

import:引用模塊,是規則內容中非常重要的組成部分,即引入所需要的Java類或方法、或者靜態方法。

import:引用模塊,是規則內容中非常重要的組成部分,即引入所需要的Java類或方法、或者靜態方法。

<code>```
package rules.rulesHelo
import com.pojo.Person
\trule “規則1"
\t\twhen
\t\t\teval(true);
\t\tthen
\t\t\tSystem.out.println(“hello world”);
\tend
\trule “規則2"
\t\twhen
\t\t\t$p:Person();
\t\tthen
\t\t\tSystem.out.println(“對象” + $p);
\tend
```/<code>
<code>```
KieServices kss = KieServices.Factory.*get*();
KieContainer kc = kss.getKieClasspathContainer();
KieSession ks = kc.newKieSession(“ksession name”);
Person p = new Person();
ks.insert(p);
int i = ks.fireAllRules();
```/<code>

Person對象即為Fact對象。

Drools配置文件

通過maven的pom.xml配置文件要用KIE操作統一管理規則,其中drools-compiler是必須引用的包,這個包的主要目的是對規則進行編譯、構建等。

Kmodule.xml是一個規則引擎的核心配置文件,類似於Spring Bean的配置文件做統一的管理。

<code>```
<kmodule>
\t<kbase>規則文件
<code>```
package rules.rulesHelo
import com.pojo.Person
\trule “規則1"
\t\twhen
\t\t\teval(true);
\t\tthen
\t\t\tSystem.out.println(“hello world”);
\tend
\trule “規則2"
\t\twhen
\t\t\t$p:Person();
\t\tthen
\t\t\tSystem.out.println(“對象” + $p);
\tend
```/<code>

標準的規則文件就是以".drl”結尾的文本文件,規則內容是放在規則文件中的,一個規則文件可以存放多個規則體。

匹配模式

<code>```
package rules.rulesHelo
import com.pojo.Person
\trule “多模式匹配"
\t\twhen
\t\t\t$p:Person(age>20)
\t\t\tOrder(customer==$customer,price>1000)
\t\tthen
\t\t\tSystem.out.println(“對象” + $p);
\tend
```/<code>

多個匹配模式是指多個Fact對象的匹配,執行匹配時只有結果為true才會被執行。上述文件中兩個對象沒有連接符,默認的約束為邏輯與(and)。

常用的語法

1. 運算符:+ - * / %等

2. contains 是用來檢查一個Fact對象的某個屬性值是否一個指定的對象值。

比如:

<code>```
$p:Person(className container $s.className);
```/<code>

3. not contains、memberOf(判斷某個對象的某個字段是否存在一個或多個集合中)、not memberOf、matches(用來對某個Fact對象的字段與標準的Java正則表達式進行相似匹配)、not matches等

4. 支持List元素操作、Set元素操作、Map元素操作等

還有其他相關語法查詢drools資料

常用的規則屬性

規則體中的屬性是通過學習規則語法的重要組成部分,是有默認值的。它的使用直接關係到規則是否可以更好地在業務場景中起到作用,是編號遼好規則的方式之一。

規則屬性共有12個,接下來取幾個常用的做說明。

1. 屬性no-loop

防止死循環,當規則通過update之類的函數修改了Fact對象時,可能使規則再被激活,從而導致死循環。默認值false。

<code>```
\trule “no-loop"
\t\t//不設置時,會出現死循環
\t\tno-loop true
\t\twhen
\t\t\t$p:Person(age>20)
\t\tthen
\t\t\t$p.setAge(30);
\t\t\tupdate($p);
\t\t\tSystem.out.println(“對象” + $p);
\tend
```/<code>

再次激活的前提條件是被修改的事實對象與規則LHS部分的約束條件是**包含關係**。

2. 屬性salience

規則體被執行的順序,每一個規則體都有一個默認的執行順序,如果不設置為salience屬性,規則體的執行順序為由上到下。比如:

<code>```
package rules.rulesHelo
import com.pojo.Person
\trule “規則1"
\t\tsalience 10
\t\twhen
\t\t\teval(true);
\t\tthen
\t\t\tSystem.out.println(“hello world”);
\tend
\trule “規則2"
\t\tsalience 20
\t\twhen
\t\t\t$p:Person();
\t\tthen
\t\t\tSystem.out.println(“對象” + $p);
\tend
```/<code>

其值越大,執行順序越高,排名越靠前,可以為負數。

3. 屬性date-effective

只有當前系統時間大於等於設置的時間或日期,規則才會被激活。

4. 屬性date-expires

只有當前系統時間小於設置的時間或日期,規則才會被激活。

5. 屬性timer

Timer屬性是一個定時器,用來控制規則的執行時間,主要有兩種寫法:

<code>timer(int: 3s)
timer (cron:0/1 * * * * ?)/<code>

重要的規則語法

1. package

同一物理目錄下的規則相關文件都會被加載到規則庫中,不同規則文件中不同的package會影響規則名稱的定義。

2. global全局變量

Global全局變量於Fact對象不同,不會因為值變化而影響到規則的再次激活。

<code>```
package rules.rulesHelo
import com.pojo.Person;

global java.lang.Integer count;
\trule “global1"
\t\twhen
\t\tthen
\t\t\tcount = 10;
\t\t\tSystem.out.println(“對象” + count);
\tend
\trule “global2"
\t\twhen
\t\tthen
\t\t\tSystem.out.println(“對象” + count);
\tend
```/<code>
<code>```
KieServices kss = KieServices.Factory.*get*();
KieContainer kc = kss.getKieClasspathContainer();
KieSession ks = kc.newKieSession(“ksession name”);
Person p = new Person();
ks.insert(p);
ks.setGlobal("count”, 0);
int i = ks.fireAllRules();
```/<code>

A.規則內部修改不會影響全局的使用。

B.全局變量如果定義成集合類或JavaBean時,在規則體RHS部分中進行修改,則規則庫或Java代碼中的值都會發生變化。

C.常量值是不能改變的;包裝類是不能改變的;JavaBean、List類的操作是可以改變的,但內存地址不會變。

3. query查詢

<code>```
package rules.rulesHelo
import com.pojo.Person;
\tquery “person is 30“
\t\tperson:Person(name==$name,age==30)
\tend
```/<code>


<code>```
KieServices kss = KieServices.Factory.*get*();
KieContainer kc = kss.getKieClasspathContainer();
KieSession ks = kc.newKieSession(“ksession name”);
Person p1 = new Person(“張三”, 20);
Person p2 = new Person(“李四”, 30);
Person p3 = new Person(“王五”, 20);
Person p3 = new Person(“馬六”, 30);
ks.insert(p1);
ks.insert(p2);
ks.insert(p3);
ks.insert(p4);
Object[] objects = new Object[]{“張三”};
QueryResults result = ks.getQueryREsults(“person is 30”, objects);
for(QueryResultsRow q : result){
\tPerson p = (Person) q.get(“person”);
\tSystem.out.println(“對象” + p);
}
ks.dispose();
```/<code>

4. function函數

<code>```
package rules.rulesHelo
\trule “function”
\t\twhen
\t\tthen
\t\t\tfunction();
\t\t\tSystem.out.println(“結束”);
\tend
Function void function(){
\tSystem.out.println(“function的值”);
}
```/<code>

Java應用中的靜態函數,可以在規則中通過import導入並使用。

Declare聲明在規則引擎中的功能主要有兩個:一是聲明新類型。二是聲明元數據類型

<code>```
package rules.rulesHelo
declare Person
\t\tname:String
\t\tage:int
end
\trule “declare1”
\t\twhen
\t\tthen
\t\t\tinsert(new Person(“張三”, 30));
\tend
\trule “declare2”
\t\twhen
\t\t\t$p:Person(name == “”張三)
\t\tthen
\t\t\tSystem.out.println(“聲明對象後insert的對象” + $p);
\tend
```/<code>

6. 規則when

規則文件中處理業務的核心在於規則體的使用,簡稱規則。

一些常用的關鍵字:複合值限制in/not in、條件元素eval、條件元素not、條件元素exists等

7. 規則then

update(Fact事實對象)、insert(new Object())插入到工作內存中、delete從工作內存刪除一個Fact事實對象等

制定規則名調用

規則文件放在同一個目錄下,或者將好多的規則體放在同一個規則文件內,執行調用規則代碼時,滿足條件的規則都會被執行,解決這一類問題的手段之一是指定要執行的規則名稱。

<code>```
package rules.rulesHelo
import com.pojo.Person
\trule “規則1"
\t\twhen
\t\t\teval(true);
\t\tthen
\t\t\tSystem.out.println(“hello world”);
\tend
\trule “規則2"
\t\twhen
\t\t\t$p:Person();
\t\tthen
\t\t\tSystem.out.println(“對象” + $p);
\tend
```/<code>
<code>```
KieServices kss = KieServices.Factory.*get*();
KieContainer kc = kss.getKieClasspathContainer();
KieSession ks = kc.newKieSession(“ksession name”);
Person p = new Person();
ks.insert(p);
int i = ks.fireAllRules(new RuleNameEqualsAgendaFilter(“規則1”));
```/<code>

只會執行規則1.

AgendaFilter的實現類不止一個,其中RuleNameEndsWithAgendaFilter是根據制定的規則名稱後綴過濾,RuleNameStartsWithAgendaFilter是根據制定的規則名稱前綴過濾,RuleNameMatchesAgendaFilter是根據制定的規則名稱正則匹配過濾。

<code>```
ks.fireAllRules(5)
```/<code>

最多執行5次。

Spring整合Drools,SpringBoot整合Drools

網上資料demo比較多,不做介紹。

KieSession狀態

KieSession有兩種狀態,俗稱有狀態和無狀態。

KieSession默認是有狀態的,通過KieContainer獲取兩種不同狀態的KieSession:

<code>```
KieContainer kc = kss.getKieClasspathContainer();
//有狀態的
KieSession ks1 = kc.newKieSession(“knXXX”);
//無狀態的
KieSession ks2 = kc.newStatelessKieSession(“knXXX”);
```/<code>

有狀態的KieSession,會在多次與規則引擎進行交互中,維護會話的狀態。定義KieSession,在kmodule.xml文件中type為stateful。

<code>```
<kmodule>
\t<kbase>
\t\t<ksession>
\t/<kbase>
/<kmodule>
```/<code>
<code>```
KieContainer kc = kss.getKieClasspathContainer();
//無狀態的
StatelessKieSession ks2 = kc.newStatelessKieSession(“knXXX”);
Ks2.execute(new Object());
```/<code>

execute執行過程:

1. 每次執行一次execute方法,都會創建一個新的StatefulKnowledgeSession;

2. KieSession執行fireAllRules()方法;

3. 每次執行完成後都會調用dispose()方法。

總結:無狀態的規則不會出現類似迭代(產生笛卡爾積)的問題,它會適時創建和清空當前的KieSession。

"/<kbase>/<kmodule>/<code>
/<kbase>/<kmodule>/<code>


分享到:


相關文章: