Drools 是用 Java 語言編寫的開放源碼規(guī)則引擎,使用 Rete 算法對所編寫的規(guī)則求值。Drools 允許使用聲明方式表達(dá)業(yè)務(wù)邏輯。可以使用非 XML 的本地語言編寫規(guī)則,從而便于學(xué)習(xí)和理解。并且,還可以將 Java 代碼直接嵌入到規(guī)則文件中,這令 Drools 的學(xué)習(xí)更加吸引人。
Drools 還具有其他優(yōu)點(diǎn):
- 非常活躍的社區(qū)支持
- 易用
- 快速的執(zhí)行速度
- 在 Java 開發(fā)人員中流行
- 與 Java Rule Engine API(JSR 94)兼容
Drools 是業(yè)務(wù)邏輯集成平臺(tái),被分為4個(gè)項(xiàng)目:
- Drools Guvnor (BRMS/BPMS):業(yè)務(wù)規(guī)則管理系統(tǒng)
- Drools Expert (rule engine):規(guī)則引擎,drools的核心部分
- Drools Flow (process/workflow):工作流引擎
- Drools Fusion (cep/temporal reasoning):事件處理
官網(wǎng):http://www.drools.org/#
官方文檔:http://www.drools.org/learn/documentation.html
Drools語法
規(guī)則文件
規(guī)則文件可以使用 .drl文件,也可以是xml文件,這里我們使用drl文件
package:對一個(gè)規(guī)則文件而言,package是必須定義的,必須放在規(guī)則文件第一行,package的名字是隨意的,不必必須對應(yīng)物理路徑,跟java的package的概念不同,這里只是邏輯上的一種區(qū)分
如:
package com.sankuai.meituan.waimai.drools.demo
import:導(dǎo)入規(guī)則文件需要使用到的外部規(guī)則文件或者變量,這里的使用方法跟java相同,但是不同于java的是,這里的import導(dǎo)入的不僅僅可以是一個(gè)類,也可以是這個(gè)類中的某一個(gè)可訪問的靜態(tài)方法
import com.drools.demo.point.PointDomain;
rule:定義一個(gè)具體規(guī)則。rule "ruleName"。一個(gè)規(guī)則可以包含三個(gè)部分:
屬性部分:
定義當(dāng)前規(guī)則執(zhí)行的一些屬性等,比如是否可被重復(fù)執(zhí)行、過期時(shí)間、生效時(shí)間等。條件部分(LHS):
定義當(dāng)前規(guī)則的條件,如 when Message(); 判斷當(dāng)前workingMemory中是否存在Message對象。結(jié)果部分(RHS):
即當(dāng)前規(guī)則條件滿足后執(zhí)行的操作,可以直接調(diào)用Fact對象的方法來操作應(yīng)用。這里可以寫普通java代碼
rule "ruleName"
no-loop true
when
$message:Message(status == 0)
then
System.out.println("fit");
$message.setStatus(1);
update($message);
end
規(guī)則詳情
屬性詳情
no-loop:
定義當(dāng)前的規(guī)則是否不允許多次循環(huán)執(zhí)行,默認(rèn)是false
;當(dāng)前的規(guī)則只要滿足條件,可以無限次執(zhí)行。什么情況下會(huì)出現(xiàn)一條規(guī)則執(zhí)行過一次又被多次重復(fù)執(zhí)行呢?drools提供了一些api,可以對當(dāng)前傳入workingMemory中的Fact對象進(jìn)行修改或者個(gè)數(shù)的增減,比如上述的update方法,就是將當(dāng)前的workingMemory中的Message類型的Fact對象進(jìn)行屬性更新,這種操作會(huì)觸發(fā)規(guī)則的重新匹配執(zhí)行,可以理解為Fact對象更新了,所以規(guī)則需要重新匹配一遍,那么疑問是之前規(guī)則執(zhí)行過并且修改過的那些Fact對象的屬性的數(shù)據(jù)會(huì)不會(huì)被重置?結(jié)果是不會(huì),已經(jīng)修改過了就不會(huì)被重置,update之后,之前的修改都會(huì)生效。當(dāng)然對Fact對象數(shù)據(jù)的修改并不是一定需要調(diào)用update才可以生效,簡單的使用set方法設(shè)置就可以完成,這里類似于java的引用調(diào)用,所以何時(shí)使用update是一個(gè)需要仔細(xì)考慮的問題,一旦不慎,極有可能會(huì)造成規(guī)則的死循環(huán)。上述的no-loop true,即設(shè)置當(dāng)前的規(guī)則,只執(zhí)行一次,如果本身的RHS部分有update等觸發(fā)規(guī)則重新執(zhí)行的操作,也不要再次執(zhí)行當(dāng)前規(guī)則。
但是其他的規(guī)則會(huì)被重新執(zhí)行,豈不是也會(huì)有可能造成多次重復(fù)執(zhí)行,數(shù)據(jù)紊亂甚至死循環(huán)?答案是使用其他的標(biāo)簽限制,也是可以控制的:lock-on-active truelock-on-active:lock-on-active true 通過這個(gè)標(biāo)簽,可以控制當(dāng)前的規(guī)則只會(huì)被執(zhí)行一次,因?yàn)橐粋€(gè)規(guī)則的重復(fù)執(zhí)行不一定是本身觸發(fā)的,也可能是其他規(guī)則觸發(fā)的,所以這個(gè)是no-loop的加強(qiáng)版
date-expires:設(shè)置規(guī)則的過期時(shí)間,默認(rèn)的時(shí)間格式:“日-月-年”
date-effective:設(shè)置規(guī)則的生效時(shí)間,時(shí)間格式同上。
duration:規(guī)則定時(shí),duration 3000,3秒后執(zhí)行規(guī)則
salience:優(yōu)先級(jí),數(shù)值越大越先執(zhí)行,這個(gè)可以控制規(guī)則的執(zhí)行順序。
條件部分- LHS
-
when:規(guī)則條件開始。條件可以單個(gè),也可以多個(gè),多個(gè)條件一次排列
如:當(dāng)前規(guī)則只有在這三個(gè)條件都匹配的時(shí)候才會(huì)執(zhí)行RHS部分
when
eval(true)
$customer:Customer()
$message:Message(status==0)
eval(true):是一個(gè)默認(rèn)的api,true 無條件執(zhí)行,類似于 while(true)
操作符:
>
、>=
、<
、<=
、==
、!=
、contains
、not contains
、memberOf
、not memberOf
、matches
、not matches
- contains: 對比是否包含操作,操作的被包含目標(biāo)可以是一個(gè)復(fù)雜對象也可以是一個(gè)簡單的值
Person( fullName not contains "Jr" )
- not contains:與contains相反。
- memberOf:判斷某個(gè)Fact屬性值是否在某個(gè)集合中,與contains不同的是他被比較的對象是一個(gè)集合,而contains被比較的對象是單個(gè)值或者對象
CheeseCounter( cheese memberOf $matureCheeses )
- not memberOf:與memberOf正好相反
- matches:正則表達(dá)式匹配
Cheese( type matches "(Buffalo)?\\S*Mozarella" )
注意:
就像在Java中,寫為字符串的正則表達(dá)式需要轉(zhuǎn)義“\”
- not matches:與matches正好相反
結(jié)果部分- RHS
當(dāng)規(guī)則條件滿足,則進(jìn)入規(guī)則結(jié)果部分執(zhí)行,結(jié)果部分可以是純java代碼
- then:
then
System.out.println("OK"); //會(huì)在控制臺(tái)打印出ok
end
- insert:往當(dāng)前workingMemory中插入一個(gè)新的Fact對象,會(huì)觸發(fā)規(guī)則的再次執(zhí)行,除非使用no-loop限定
- update:更新
- modify:修改,與update語法不同,結(jié)果都是更新操作
- retract:刪除
rule "Rule 03"
when
$number : Number( )
not Number( intValue < $number.intValue )
then
System.out.println("Number found with value: " + $number.intValue() );
retract( $number );
end
Drools關(guān)鍵詞
關(guān)鍵詞 | 描述 | 詳情 |
---|---|---|
lock-on-active | ||
date-effective | ||
date-expires | ||
no-loop | ||
auto-focus | ||
activation-group | ||
agenda-group | ||
ruleflow-group | ||
entry-point | ||
duration | ||
package | ||
import | ||
dialect | ||
salience | ||
enabled | ||
attributes | ||
rule | ||
extend | ||
when | ||
then | ||
template | ||
query | ||
declare | ||
function | ||
global | ||
eval | ||
not | ||
in | ||
or | ||
and | ||
exists | ||
forall | ||
accumulate | ||
collect | ||
from | ||
action | ||
reverse | ||
result | ||
end | ||
over | ||
init | - |
Drools方法定義
- function
function String hello(String name) {
return "Hello "+name+"!";
}
Drools聲明類型
- declare:聲明類型
- 聲明Class、Enum etc類型
- 聲明元數(shù)據(jù)
聲明類類型
declare Address
number : int
streetName : String
city : String
end
聲明枚舉類型
declare enum DaysOfWeek
SUN("Sunday"),MON("Monday"),TUE("Tuesday"),WED("Wednesday"),THU("Thursday"),FRI("Friday"),SAT("Saturday");
fullName : String
end
聲明元數(shù)據(jù)類型
元數(shù)據(jù)可以被分配給在Drools中幾個(gè)不同的結(jié)構(gòu):
- fact types
- fact attributes
- rules
定義格式:
@metadata_key(metadata_value)
例子:
@author( Bob )
import java.util.Date
declare Person
@author( Bob )
@dateOfCreation*( 01-Feb-2009 )
name : String @key @maxLength*( 30 )
dateOfBirth : Date address : Address
end
聲明元數(shù)據(jù)類級(jí)別 關(guān)鍵詞
@role( <fact | event> )
import some.package.StockTick
declare StockTick
@role ( event )
end
@typesafe( <boolean> )
@timestamp( <attribute name> )
declare VoiceCall
@role( event )
@timestamp( callDateTime )
end
@duration( <attribute name> )
@expires( <time interval> )
@propertyChangeSupport
@propertyReactive
聲明元數(shù)據(jù)屬性級(jí)別 關(guān)鍵詞
@key
兩個(gè)方面影響:
根據(jù)@key作為類標(biāo)識(shí)符,類比較以 @key 的字段為準(zhǔn)
根據(jù)@key字段生成構(gòu)造函數(shù)
declare Person
firstName : String @key
lastName : String @key
age : int
end
@position
declare Cheese
name : String @position(1)
shop : String @position(2)
price : int @position(0)
end
設(shè)計(jì)
Drools vs ILog vs Jess vs Mandarax
優(yōu)點(diǎn) | ||
---|---|---|
Drools | 開源、社區(qū)非常活躍、易使用、免費(fèi)、JSR94兼容(JSR94是Java Rule Engine API)、支持Java、強(qiáng)大的工具集 | 只支持一種推理方式、安全性不夠 |
ILog | 性能高(電信領(lǐng)域使用)、易使用 | 商業(yè)產(chǎn)品、不開源 |
Jess | 支持2種推理方式(正向鏈和反向鏈)、很強(qiáng)的表示、推理能力、支持AOP | 不開源、無規(guī)則管理工具、不易使用 |
Mandarax | 開源、免費(fèi)、支持Java | JSR94不兼容(JSR94是Java Rule Engine API)、已經(jīng)不更新、社區(qū)不活躍、并且文檔不全 |
推理方式
- 正向鏈推理:一條由問題開始搜索,并得到其解答的鏈稱為正向鏈推理。
- 反向鏈推理:一條由假設(shè)回推到支持該假設(shè)的事實(shí)的鏈稱為反向鏈推理。
作者 @九都散人
2016 年 5月 6日
參考:
Jess 反向鏈推理機(jī)理及診斷專家系統(tǒng)開發(fā)模式研究
http://www.docin.org/p-86340503.html
Drools 6.4 Final 文檔