美文网首页
Drools详解

Drools详解

作者: Tiger_Lam | 来源:发表于2021-11-10 16:19 被阅读0次

    Drools规则引擎的结构示意图

    企业微信截图_16365257039225.png image.png

    Drools相关概念

    • 事实(Fact):对象之间及对象属性之间的关系
    • 规则(rule):是由条件和结论构成的推理语句,一般表示为if...Then。一个规则的if部分称为LHS,then部分称为RHS。
    • 模式(module):就是指IF语句的条件。这里IF条件可能是有几个更小的条件组成的大条件。模式就是指的不能再继续分割下去的最小的原子条件。

    Drools原理

    DRL解释执行流程

    Drools规则是在Java应用程序上运行的,其要执行的步骤顺序由代码确定。为了实现这一点,Drools规则引擎将业务规则转换成执行树,如下图所示:
    image.png
    image.png
    当到达一个Fact与规则相匹配的节点时,规则评估会将规则操作与触发数据添加到一个叫作议程(Agenda)的组件中,如果同一个Fact与多个规则相匹配,就认为这些规则是冲突的,议程(Agenda)使用冲突解决策略(Conflict Resolution strategy)管理这些冲突规则的执行顺序。整个生命周期中,规则评估与规则执行之间有着明确的分割。规则操作的执行可能会导致Fact的更新,从而与其他规则相匹配,导致它们的触发,称之为前向链接。

    规则引擎工作方式

    image.png

    1.Pattern Matching:对新的数据和被修改的数据进行规则的匹配称为模式匹配.
    2.Production Memory:被访问的规则.
    3.Agenda:负责具体执行推理算法中被激发规则的结论部分,同时 Agenda 通过冲突决策策略管理这些冲突规则的执行顺序.

    Drools 中规则冲突决策策略有:
    • (1) 优先级策略
    • (2) 复杂度优先策略
    • (4) 广度策略
    • (5) 深度策略
    • (6) 装载序号策略
    • (7) 随机策略
      4.Working Memory:被推理机进行匹配的数据.
      5.Inference Engine:进行匹配的引擎称为推理机.
      推理机所采用的模式匹配算法有下列几种:
      (1) Linear
      (2) RETE
      (3) Treat
      (4) Leaps
    Drools有专门的规则语法drl,就是专门描述活动的规则是如何执行的
    ## rule.drl文件内容如下
    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("无赠品");
            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雷达限量表");
            doSth($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属性的值是一个数字,数字越来优先级越高,值也可以是负数。默认为0,如果不设置规则的salience属性,那么执行顺序是随机的。
    • when 条件语句,就是当到达什么条件的时候
    • then 根据条件的结果,来执行什么动作
    • end 规则结束
    这里需要有一个配置文件告诉代码规则文件drl在哪里,在drools中这个文件就是kmodule.xml,放置到resources/META-INF目录下。
    kmodule.xml内容如下:
    
    <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>
    

    说明:

    • Kmodule 中可以包含一个到多个 kbase,分别对应 drl 的规则文件。

    • Kbase 需要一个唯一的 name,可以取任意字符串。

    • packages 为drl文件所在resource目录下的路径。注意区分drl文件中的package与此处的package不一定相同。多个包用逗号分隔。默认情况下会扫描 resources目录下所有(包含子目录)规则文件。

    • kbase的default属性,标示当前KieBase是不是默认的,如果是默认的则不用名称就可以查找到该 KieBase,但每个 module 最多只能有一个默认 KieBase。

    • kbase 下面可以有一个或多个 ksession,ksession 的 name 属性必须设置,且必须唯一。

    规则中的条件操作符

    Drools提供了十二中类型比较操作符:< 、<=、>、>=、==、!=、contains、not contains、memberOf、not memberOf、matches、not matches,并且这些条件都可以组合使用。
    • 条件组合:各种操作符可以组合使用
    • Fact对象私有属性的操作:RHS中对Fact对象private属性的操作必须使用getter和setter方法,而RHS中则必须要直接用.的方法去使用.
    • contains:对比是否包含操作,操作的被包含目标可以是一个复杂对象也可以是一个简单的值。
    • matches:正则表达式匹配,与java不同的是,不用考虑'/'的转义问题
    • memberOf:判断某个Fact属性值是否在某个集合中,与contains不同的是他被比较的对象是一个集合,而contains被比较的对象是单个值或者对象。

    规则中的结果部分

    • insert:往当前workingMemory中插入一个新的Fact对象,会触发规则的再次执行,除非使用no-loop限定;
    • update:更新
    • modify:修改,与update语法不同,结果都是更新操作
    • retract:删除
    • function:定义一个方法,如:
      function void console { System.out.println(); StringUtils.getId();// 调用外部静态方法,StringUtils必须使用import导入,getId()必须是静态方法}
    • declare:可以在规则文件中定义一个class,使用起来跟普通java对象相似,你可以在RHS部分中new一个并且使用getter和setter方法去操作其属性。
    Demo
    工程结构
    image.png
    依赖文件pom.xml
    
    <!--规则引擎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燃气表协议");
            System.out.println("触发规则1:"+$protocol.getData());
         end
    
    rule "jkstd"
        when
            $protocol : ProtocolType(data matches "18.*26",length == 10)
        then
            $protocol.setType("xx水表标准协议");
            System.out.println("触发规则2:"+$protocol.getData());
         end
    
    测试程序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());
        }
    
    }
    
    
    触发规则1:1A1212345616
    ProtocolType(data=1A1212345616,length=12,type=xx燃气表协议)
    2020-08-19 18:21:35 [main] [io.example.demo.Main.test(Main.java:35)] - [INFO] xx燃气表协议
    
    Process finished with exit code 0
    

    Drools中的坑

    Drools与SpringBoot集成时,与热部署工具spring-boot-devtools存在类加载器冲突的问题,会导致所有的规则失效。在drools的官方网站中有人提出了这个问题,并认为是个bug,但是drools的开发者认为这不是一个bug。

    drools用的是lancher classloader,而devtools会把我们自己编写的model认为是会随时更改的类,所以采用的是Restart ClassLoader类加载器进行加载(为了快速进行热部署),devtools会有两个类加载器,一个Classloader加载那些不会改变的类(第三方Jar包),另一个ClassLoader加载会更改的类,称为 Restart ClassLoader。当采用Launcher ClassLoader加载的A 类与Restart Class Loader的A类进行对比时,发现不一致,所以drools引擎自然无法进行识别。

    所以解决办法就是将devtools的maven依赖去掉即可,或者采用drools官网中说明的其它方法。

    相关文章

      网友评论

          本文标题:Drools详解

          本文链接:https://www.haomeiwen.com/subject/tsabzltx.html