Drools詳解

Drools規(guī)則引擎的結(jié)構(gòu)示意圖

企業(yè)微信截圖_16365257039225.png
image.png

Drools相關(guān)概念

  • 事實(shí)(Fact):對(duì)象之間及對(duì)象屬性之間的關(guān)系
  • 規(guī)則(rule):是由條件和結(jié)論構(gòu)成的推理語(yǔ)句,一般表示為if...Then屁桑。一個(gè)規(guī)則的if部分稱為L(zhǎng)HS,then部分稱為RHS。
  • 模式(module):就是指IF語(yǔ)句的條件。這里IF條件可能是有幾個(gè)更小的條件組成的大條件互婿。模式就是指的不能再繼續(xù)分割下去的最小的原子條件。

Drools原理

DRL解釋執(zhí)行流程

Drools規(guī)則是在Java應(yīng)用程序上運(yùn)行的,其要執(zhí)行的步驟順序由代碼確定。為了實(shí)現(xiàn)這一點(diǎn),Drools規(guī)則引擎將業(yè)務(wù)規(guī)則轉(zhuǎn)換成執(zhí)行樹(shù),如下圖所示:
image.png

image.png
當(dāng)?shù)竭_(dá)一個(gè)Fact與規(guī)則相匹配的節(jié)點(diǎn)時(shí)勋颖,規(guī)則評(píng)估會(huì)將規(guī)則操作與觸發(fā)數(shù)據(jù)添加到一個(gè)叫作議程(Agenda)的組件中,如果同一個(gè)Fact與多個(gè)規(guī)則相匹配,就認(rèn)為這些規(guī)則是沖突的,議程(Agenda)使用沖突解決策略(Conflict Resolution strategy)管理這些沖突規(guī)則的執(zhí)行順序。整個(gè)生命周期中,規(guī)則評(píng)估與規(guī)則執(zhí)行之間有著明確的分割勋锤。規(guī)則操作的執(zhí)行可能會(huì)導(dǎo)致Fact的更新,從而與其他規(guī)則相匹配,導(dǎo)致它們的觸發(fā),稱之為前向鏈接饭玲。

規(guī)則引擎工作方式

image.png

1.Pattern Matching:對(duì)新的數(shù)據(jù)和被修改的數(shù)據(jù)進(jìn)行規(guī)則的匹配稱為模式匹配.
2.Production Memory:被訪問(wèn)的規(guī)則.
3.Agenda:負(fù)責(zé)具體執(zhí)行推理算法中被激發(fā)規(guī)則的結(jié)論部分,同時(shí) Agenda 通過(guò)沖突決策策略管理這些沖突規(guī)則的執(zhí)行順序.

Drools 中規(guī)則沖突決策策略有:
  • (1) 優(yōu)先級(jí)策略
  • (2) 復(fù)雜度優(yōu)先策略
  • (4) 廣度策略
  • (5) 深度策略
  • (6) 裝載序號(hào)策略
  • (7) 隨機(jī)策略
    4.Working Memory:被推理機(jī)進(jìn)行匹配的數(shù)據(jù).
    5.Inference Engine:進(jìn)行匹配的引擎稱為推理機(jī).
    推理機(jī)所采用的模式匹配算法有下列幾種:
    (1) Linear
    (2) RETE
    (3) Treat
    (4) Leaps
Drools有專門的規(guī)則語(yǔ)法drl,就是專門描述活動(dòng)的規(guī)則是如何執(zhí)行的
## rule.drl文件內(nèi)容如下
package com.alibaba.rules

import com.alibaba.Order

rule "zero"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(point <= 300)
    then
        System.out.println("無(wú)贈(zèng)品");
        doSth($s);
end

rule "giftOne"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 300 && amout <= 500)
    then
        System.out.println("Dior限量口紅");
        doSth($s);
end

rule "giftTwo"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 500 && amout <= 700)
    then
        System.out.println("TF限量口紅");
        doSth($s);
end

rule "giftThree"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 700 && amout <= 900)
    then
        System.out.println("SK-II套裝");
        doSth($s);
end

rule "giftFour"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 900 && amout <= 1100)
    then
        System.out.println("MCM雙肩包");
        doSth($s);
end

rule "giftFive"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 1100)
    then
        System.out.println("RADO雷達(dá)限量表");
        doSth($s);
end 

說(shuō)明:

  • package 與Java語(yǔ)言類似,drl的頭部需要有package和import的聲明,package不必和物理路徑一致叁执。
  • import 導(dǎo)出java Bean的完整路徑,也可以將Java靜態(tài)方法導(dǎo)入調(diào)用茄厘。
  • rule 規(guī)則名稱,需要保持唯一,可以無(wú)限次執(zhí)行
  • no-loop 定義當(dāng)前的規(guī)則是否不允許多次循環(huán)執(zhí)行,默認(rèn)是false,也就是當(dāng)前的規(guī)則只要滿足條件,可以無(wú)限次執(zhí)行矮冬。
  • lock-on-active 將lock-on-active屬性的值設(shè)置為true,可避免因某些Fact對(duì)象被修改而使已經(jīng)執(zhí)行過(guò)的規(guī)則再次被激活執(zhí)行。
  • salience用來(lái)設(shè)置規(guī)則執(zhí)行的優(yōu)先級(jí),salience屬性的值是一個(gè)數(shù)字,數(shù)字越來(lái)優(yōu)先級(jí)越高,值也可以是負(fù)數(shù)次哈。默認(rèn)為0,如果不設(shè)置規(guī)則的salience屬性,那么執(zhí)行順序是隨機(jī)的胎署。
  • when 條件語(yǔ)句,就是當(dāng)?shù)竭_(dá)什么條件的時(shí)候
  • then 根據(jù)條件的結(jié)果,來(lái)執(zhí)行什么動(dòng)作
  • end 規(guī)則結(jié)束
這里需要有一個(gè)配置文件告訴代碼規(guī)則文件drl在哪里,在drools中這個(gè)文件就是kmodule.xml窑滞,放置到resources/META-INF目錄下琼牧。
kmodule.xml內(nèi)容如下:

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.drools.org/xsd/kmodule">
  <configuration>
    <property key="drools.evaluator.supersetOf" value="org.mycompany.SupersetOfEvaluatorDefinition"/>
  </configuration>
  <kbase name="KBase1" default="true" eventProcessingMode="cloud" equalsBehavior="equality" declarativeAgenda="enabled" packages="org.domain.pkg1">
    <ksession name="KSession2_1" type="stateful" default="true"/>
    <ksession name="KSession2_2" type="stateless" default="false" beliefSystem="jtms"/>
  </kbase>
  <kbase name="KBase2" default="false" eventProcessingMode="stream" equalsBehavior="equality" declarativeAgenda="enabled" packages="org.domain.pkg2, org.domain.pkg3" includes="KBase1">
    <ksession name="KSession3_1" type="stateful" default="false" clockType="realtime">
      <fileLogger file="drools.log" threaded="true" interval="10"/>
      <workItemHandlers>
        <workItemHandler name="name" type="org.domain.WorkItemHandler"/>
      </workItemHandlers>
      <calendars>
        <calendar name="monday" type="org.domain.Monday"/>
      </calendars>
      <listeners>
        <ruleRuntimeEventListener type="org.domain.RuleRuntimeListener"/>
        <agendaEventListener type="org.domain.FirstAgendaListener"/>
        <agendaEventListener type="org.domain.SecondAgendaListener"/>
        <processEventListener type="org.domain.ProcessListener"/>
      </listeners>
    </ksession>
  </kbase>
</kmodule>

說(shuō)明:

  • Kmodule 中可以包含一個(gè)到多個(gè) kbase,分別對(duì)應(yīng) drl 的規(guī)則文件。

  • Kbase 需要一個(gè)唯一的 name,可以取任意字符串哀卫。

  • packages 為drl文件所在resource目錄下的路徑巨坊。注意區(qū)分drl文件中的package與此處的package不一定相同。多個(gè)包用逗號(hào)分隔此改。默認(rèn)情況下會(huì)掃描 resources目錄下所有(包含子目錄)規(guī)則文件趾撵。

  • kbase的default屬性,標(biāo)示當(dāng)前KieBase是不是默認(rèn)的,如果是默認(rèn)的則不用名稱就可以查找到該 KieBase,但每個(gè) module 最多只能有一個(gè)默認(rèn) KieBase。

  • kbase 下面可以有一個(gè)或多個(gè) ksession,ksession 的 name 屬性必須設(shè)置,且必須唯一共啃。

規(guī)則中的條件操作符

Drools提供了十二中類型比較操作符:< 占调、<=、>移剪、>=究珊、==、!=纵苛、contains剿涮、not contains、memberOf赶站、not memberOf、matches纺念、not matches贝椿,并且這些條件都可以組合使用。
  • 條件組合:各種操作符可以組合使用
  • Fact對(duì)象私有屬性的操作:RHS中對(duì)Fact對(duì)象private屬性的操作必須使用getter和setter方法陷谱,而RHS中則必須要直接用.的方法去使用.
  • contains:對(duì)比是否包含操作烙博,操作的被包含目標(biāo)可以是一個(gè)復(fù)雜對(duì)象也可以是一個(gè)簡(jiǎn)單的值。
  • matches:正則表達(dá)式匹配烟逊,與java不同的是渣窜,不用考慮'/'的轉(zhuǎn)義問(wèn)題
  • memberOf:判斷某個(gè)Fact屬性值是否在某個(gè)集合中,與contains不同的是他被比較的對(duì)象是一個(gè)集合宪躯,而contains被比較的對(duì)象是單個(gè)值或者對(duì)象乔宿。

規(guī)則中的結(jié)果部分

  • insert:往當(dāng)前workingMemory中插入一個(gè)新的Fact對(duì)象,會(huì)觸發(fā)規(guī)則的再次執(zhí)行访雪,除非使用no-loop限定详瑞;
  • update:更新
  • modify:修改掂林,與update語(yǔ)法不同,結(jié)果都是更新操作
  • retract:刪除
  • function:定義一個(gè)方法坝橡,如:
    function void console { System.out.println(); StringUtils.getId();// 調(diào)用外部靜態(tài)方法泻帮,StringUtils必須使用import導(dǎo)入,getId()必須是靜態(tài)方法}
  • declare:可以在規(guī)則文件中定義一個(gè)class计寇,使用起來(lái)跟普通java對(duì)象相似锣杂,你可以在RHS部分中new一個(gè)并且使用getter和setter方法去操作其屬性。
Demo
工程結(jié)構(gòu)
image.png
依賴文件pom.xml

<!--規(guī)則引擎drools-->
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-core</artifactId>
    <version>7.0.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.0.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-decisiontables</artifactId>
    <version>7.0.0.Final</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-templates</artifactId>
    <version>7.0.0.Final</version>
</dependency>

<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-api</artifactId>
    <version>7.0.0.Final</version>
</dependency>
配置文件kmodule.drl

package com.rules
import model.ProtocolType
dialect "java"

rule "jk16"
    when
        $protocol : ProtocolType(data matches "^1A12.*16$" )
    then
        $protocol.setType("xx燃?xì)獗韰f(xié)議");
        System.out.println("觸發(fā)規(guī)則1:"+$protocol.getData());
     end

rule "jkstd"
    when
        $protocol : ProtocolType(data matches "18.*26",length == 10)
    then
        $protocol.setType("xx水表標(biāo)準(zhǔn)協(xié)議");
        System.out.println("觸發(fā)規(guī)則2:"+$protocol.getData());
     end
測(cè)試程序Main.java

@Slf4j
public class Main {

    @Test
    public void test() {

        KieServices ks = KieServices.Factory.get();
        KieContainer kContainer = ks.getKieClasspathContainer();
        KieSession kSession = kContainer.newKieSession("ksession-rules");

        ProtocolType protocolType = new ProtocolType();
        protocolType.setData("1A1212345616");
        protocolType.setLength(12);
        kSession.insert(protocolType);
        kSession.fireAllRules();
        kSession.dispose();
        System.out.println(protocolType);

        log.info(protocolType.getType());
    }

}

觸發(fā)規(guī)則1:1A1212345616
ProtocolType(data=1A1212345616,length=12,type=xx燃?xì)獗韰f(xié)議)
2020-08-19 18:21:35 [main] [io.example.demo.Main.test(Main.java:35)] - [INFO] xx燃?xì)獗韰f(xié)議

Process finished with exit code 0

Drools中的坑

Drools與SpringBoot集成時(shí)番宁,與熱部署工具spring-boot-devtools存在類加載器沖突的問(wèn)題元莫,會(huì)導(dǎo)致所有的規(guī)則失效。在drools的官方網(wǎng)站中有人提出了這個(gè)問(wèn)題贝淤,并認(rèn)為是個(gè)bug柒竞,但是drools的開(kāi)發(fā)者認(rèn)為這不是一個(gè)bug。

drools用的是lancher classloader,而devtools會(huì)把我們自己編寫(xiě)的model認(rèn)為是會(huì)隨時(shí)更改的類播聪,所以采用的是Restart ClassLoader類加載器進(jìn)行加載(為了快速進(jìn)行熱部署)朽基,devtools會(huì)有兩個(gè)類加載器,一個(gè)Classloader加載那些不會(huì)改變的類(第三方Jar包)离陶,另一個(gè)ClassLoader加載會(huì)更改的類稼虎,稱為 Restart ClassLoader。當(dāng)采用Launcher ClassLoader加載的A 類與Restart Class Loader的A類進(jìn)行對(duì)比時(shí)招刨,發(fā)現(xiàn)不一致霎俩,所以drools引擎自然無(wú)法進(jìn)行識(shí)別。

所以解決辦法就是將devtools的maven依賴去掉即可沉眶,或者采用drools官網(wǎng)中說(shuō)明的其它方法打却。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谎倔,隨后出現(xiàn)的幾起案子柳击,更是在濱河造成了極大的恐慌,老刑警劉巖片习,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捌肴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡藕咏,警方通過(guò)查閱死者的電腦和手機(jī)状知,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)孽查,“玉大人饥悴,你說(shuō)我怎么就攤上這事。” “怎么了铺坞?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵起宽,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我济榨,道長(zhǎng)坯沪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任擒滑,我火速辦了婚禮腐晾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丐一。我一直安慰自己藻糖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布库车。 她就那樣靜靜地躺著巨柒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柠衍。 梳的紋絲不亂的頭發(fā)上洋满,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音珍坊,去河邊找鬼牺勾。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阵漏,可吹牛的內(nèi)容都是我干的驻民。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼履怯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼回还!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起叹洲,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤柠硕,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后疹味,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體仅叫,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帜篇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年糙捺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笙隙。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡洪灯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出竟痰,到底是詐尸還是另有隱情签钩,我是刑警寧澤掏呼,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站铅檩,受9級(jí)特大地震影響憎夷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昧旨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一拾给、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兔沃,春花似錦蒋得、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至怕吴,卻和暖如春窍侧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背械哟。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工疏之, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人暇咆。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓锋爪,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親爸业。 傳聞我的和親對(duì)象是個(gè)殘疾皇子其骄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容