JDK21 与 Drools 9.44.0.Final 规则引擎

发布时间:2023年12月27日

一.规则引擎基本理解

前言:规则引擎的用途,可以通过修改规则配置,从而动态调整业务规则,搭配可视化工具,适合于业务人员随时调整规则。相对于 HardCode , 虽然需要一定学习成本,但具备以下优势

  • 简化系统架构,提高可维护性
  • 规则轻量、多样化
  • 任意修改、任意加载,动态处理
  • 脚本支持、复杂逻辑支持、可扩展与复用
  • 上下文隔离,有状态与无状态会话

适用场景

  • 动作监听
  • 打折促销
  • 数据过滤
  • 消息路由
  • 会员管理
  • 积分管理

二.Drools 基本概念

Drools 是 JBoss 旗下 KIE (Knowledge is everything)体系的一个子产品,基于 Java 语言开发,用于进行规则解析与执行。

1.kmodule默认配置文件定义

默认配置文件:src/main/resources/META-INF/kmodule.xml

kbase 标签

属性含义
namekbase 名称,全局唯一,不允许重复,可以理解为工作空间或命名空间
includes包含,用于将多个kbase封装到一起,通过【,】分割
packages包名,即规则文件的位置,通过【,】分割可配置多个
default是否为默认命名空间
equalsBehavior相等的判断逻辑,用代码说明就是 == 和 equals 的区别,即 identity 和 equality
eventProcessingMode事件模式,stream 模式允许进行时间推理,cloud为普通fact
declarativeAgendadisable或enable 用于控制规则间逻辑关系

ksession 标签

属性含义
nameksession 名称,全局唯一,不允许重复
typestateful 有状态,对 working memory 内数据多次处理 stateless 无状态
default是否为默认会话
clockType时钟类型,realtime 系统时钟 pseudo 伪时钟,可用于单元测试
beliefSystem信仰系统,用于控制资源访问和冲突

2.规则文件

规则配置文件位置:rc/main/resources/*.drl

规则文件定义

关键字含义
package包名,同一个包下的查询或者函数可直接调用
import导入类或静态方法
global定义全局变量
function自定义函数
query查询
rule - end规则体

规则体定义

关键字含义
rule规则体定义开始关键字
attribute规则属性
when关键字,后面跟规则条件,空视为 true
then关键字,后面跟规则处理,空则不处理
end规则体定义结束关键字

规则体条件定义

符号含义
<小于
<=小于等于
==等于
>=大于等于
>大于
!=不等于
contains包含
not contains不包含
memberOf属于
not memberOf不属于
matches正则匹配
not matches正则不匹配

3.引擎简介

Drools 规则引擎构成

  • Working Memory(工作内存)
  • Rule Base(规则库)
  • Inference Engine(推理引擎)

其中推理引擎又包括:

  • Pattern Matcher(匹配器)
  • Agenda(议程)
  • Execution Engine(执行引擎)

4.概念说明

概念含义
KieServicesKIE的顶层抽象,用于创建、管理和获取KieContainer、KieSession等
KieContainerkbase 实例化后的一个容器,一组规则实例
KieSession用于与 kbase 实例交互的一个会话
kieModules通过 xml 配置进行 kbase、ksession 的声明
KieRepository用于存放 KieModule 的单例对象,即仓库
KieProject初始化 KieModule,并将其存放到KieRepository仓库中,然后 KieContainer 可以通过 KieProject 来查找 KieModule ,并根据这些信息构造KieBase 和 KieSession
ClasspathKieProjectKieProject的一个具体实现,从根目录加载kmodule.xml配置,从而初始化一个 kiemodule
Working Memory工作内存,将待处理数据插入进去,drools 进行处理
Fact一个普通的 JavaBean 插入到 Working Memory 后,drools 称之为Fact 对象
Rule Base规则库
Pattern Matcher将规则库规则与工作内存中事实对象进行匹配,成功则将规则放到议程
Agenda议程,存放上一步通过匹配器进行模式匹配后被激活的规则
Execution Engine执行议程内激活的规则

三.Drools 示例

Drools 官方文档

不同版本差异较大,本示例对应版本如下

环境版本
JDKjdk-21.0.1
Drools9.44.0.Final

工程结构

在这里插入图片描述

Pom 依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>drools-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <drools.version>9.44.0.Final</drools.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.drools</groupId>
                <artifactId>drools-bom</artifactId>
                <type>pom</type>
                <scope>import</scope>
                <version>${drools.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-engine</artifactId>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-mvel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-model-compiler</artifactId>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-xml-support</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>

    </dependencies>

</project>

kmodule.xml 配置,默认在如下目录 src\main\resources\META-INF

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">

    <!--
        name: kbase 名称全局唯一
        packages: 规则文件包目录
        default: 指定为默认 kbase
    -->
    <kbase name="my_kbase" packages="rules.*" default="true">
        <!--
            name: ksession 名称 kbase 下唯一
            default: 指定为默认 ksession
            clockType: 时钟类型 系统时钟或测试的伪时钟
        -->
        <ksession name="my_ks" clockType="realtime" default="true"/>
    </kbase>
</kmodule>

1.Attribute 关键字和说明

关键字含义示例
salience整型,规则优先级salience 10
enabled布尔值,规则是否可用enabled true
date-effective字符串,规则在某个日期或时间后可用date-effective “4-Sep-2018”
date-expires字符串,规则在某个日期或时间后不可用date-expires “4-Oct-2018”
no-loop布尔值,表示当前规则是否允许被循环激活no-loop true
activation-group用于控制所属 internalMatch 分组,组内只有一个规则能激活activation-group “GroupName”
duration长整型,经过多少毫秒后规则满足条件时仍可用,类似于休眠指定时间duration 10000
timer定时配置timer ( int: 30s 5m ) 延迟30秒后每5分钟
timer ( cron:* 0/15 * * * ? ) Cron 表达式
calendar定时配置calendars “* * 0-7,18-23 ? * *” Quartz 表达式
auto-focus布尔值,当前规则执行时自动提交给所属议程组auto-focus true
lock-on-active布尔值,避免规则流组或议程组立即读取规则的更新lock-on-active true
dialect代码表达式方言,JAVA 或 MVELdialect “JAVA”

1.优先级

新建 drl 文件

package rules.salience

rule "salience-1"
    salience 1
    when
    then
        System.out.println(1);
end
package rules.salience

rule "salience-2"
    salience 2
    when
    then
        System.out.println(2);
end

package rules.salience

rule "salience-3"
    salience 3
    when
    then
        System.out.println(3);
end

测试代码:

package org.example;


import org.kie.api.KieServices;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.conf.TimedRuleExecutionOption;
import org.kie.api.runtime.rule.AgendaFilter;

/**
 * @author Administrator
 */
public class DroolsApp {
    public static void main(String[] args) {

        //KIE 服务
        KieServices services = KieServices.Factory.get();
        //KIE 容器
        KieContainer container = services.getKieClasspathContainer();
        //会话配置
        KieSessionConfiguration ksc = KieServices.Factory.get().newKieSessionConfiguration();
        ksc.setOption( TimedRuleExecutionOption.YES );

        String ksName = "my_ks";

        //获取新会话
        KieSession session = container.newKieSession(ksName,ksc);
        //激活所有规则
        session.fireAllRules();
        //最多激活两个
//        session.fireAllRules(2);

        //过滤规则
//        AgendaFilter filter = match -> {
//            Rule rule = match.getRule();
//            if (rule.getName().equals("salience-2")){
//                return true;
//            }
//            return false;
//        };
//        session.fireAllRules(filter);

        //销毁
        //session.destroy();
    }
}

结果

在这里插入图片描述

2.是否可用

将上面的 salience_2.drl 修改为

package rules.salience

rule "salience-2"
    enabled false
    salience 2
    when
    then
        System.out.println(2);
end

重新执行结果,规则 2 已不可用

在这里插入图片描述

3.生效时间控制

将 salience_1.drl 修改为如下,注意时间格式 [ d-MMM-yyyy ]

package rules.salience

rule "salience-1"
    date-effective "17-dec-2023"
    salience 1
    when
    then
        System.out.println(1);
end

将 salience_3.drl 修改为如下,注意时间格式 [ d-MMM-yyyy ]

package rules.salience

rule "salience-3"
    date-expires "17-dec-2023"
    salience 3
    when
    then
        System.out.println(3);
end

执行结果 salience_1.drl 开始生效 salience_3.drl 开始失效

在这里插入图片描述

4.NO-LOOP 死循环控制

定义一个测试用的对象

package org.entity;

/**
 * @author zhuwd && moon
 * @Description
 * @create 2023-12-17 15:39
 */

public class NoLoopEntity {

    private Integer num;

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer age) {
        this.num = age;
    }
}

新建目录 rules\no_loop ,并定义规则文件

package rules.no_loop
import org.entity.NoLoopEntity

rule "no-loop-1"
    no-loop false

    when
        //判断并取值
        $person : NoLoopEntity(num > 0 , $tmp : (num - 1))
    then
        //设置 num
        $person.setNum($tmp);
        //更新
        update($person);
        System.out.println($tmp);
end

测试代码

package org.example;


import org.entity.NoLoopEntity;
import org.kie.api.KieServices;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.conf.TimedRuleExecutionOption;
import org.kie.api.runtime.rule.AgendaFilter;

/**
 * @author Administrator
 */
public class DroolsApp {
    public static void main(String[] args) {

        //KIE 服务
        KieServices services = KieServices.Factory.get();
        //KIE 容器
        KieContainer container = services.getKieClasspathContainer();
        //会话配置
        KieSessionConfiguration ksc = KieServices.Factory.get().newKieSessionConfiguration();
        ksc.setOption( TimedRuleExecutionOption.YES );

        String ksName = "my_ks";

        //获取新会话
        KieSession session = container.newKieSession(ksName,ksc);
        //过滤规则
        AgendaFilter filter = match -> {
            Rule rule = match.getRule();
            if (rule.getName().equals("no-loop-1")){
                return true;
            }
            return false;
        };

        NoLoopEntity noLoop = new NoLoopEntity();
        noLoop.setNum(10);

        session.insert(noLoop);
        session.fireAllRules(filter);

        //销毁
        //session.destroy();
        
    }
}

no-loop false 允许循环结果如下

在这里插入图片描述

no-loop true 不允许循环结果如下

在这里插入代码片

在这里插入图片描述

5.分组测试

新建包 rules\group 并定义规则

package rules.group

rule "group-1"
    activation-group "my-group-1"
    when
    then
        System.out.println("Group:"+1);
end

rule "group-2"
    activation-group "my-group-2"
    when
    then
        System.out.println("Group:"+2);
end

rule "group-3"
    activation-group "my-group-3"
    when
    then
        System.out.println("Group:"+3);
end

测试类


package org.example;


import org.kie.api.KieServices;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.conf.TimedRuleExecutionOption;
import org.kie.api.runtime.rule.AgendaFilter;

/**
 * @author Administrator
 */
public class DroolsApp {
    public static void main(String[] args) {

        //KIE 服务
        KieServices services = KieServices.Factory.get();
        //KIE 容器
        KieContainer container = services.getKieClasspathContainer();
        //会话配置
        KieSessionConfiguration ksc = KieServices.Factory.get().newKieSessionConfiguration();
        ksc.setOption( TimedRuleExecutionOption.YES );

        String ksName = "my_ks";

        //获取新会话
        KieSession session = container.newKieSession(ksName,ksc);
        //过滤规则
        AgendaFilter filter = match -> {
            Rule rule = match.getRule();
            if (rule.getName().startsWith("group-")){
                return true;
            }
            return false;
        };

        session.fireAllRules(filter);

        //销毁
        //session.destroy();

    }
}

测试结果

在这里插入图片描述

将上面三个规则体的 activation-group 统一修改为 my-group

在这里插入图片描述

6.Duration 休眠测试

新建包 rules\duration 并定义规则

package rules.duration
global java.lang.Integer num

rule "my-duration"
    duration 1000
    when
        Integer( $iv: intValue > num )
    then
        System.out.println("duration:" + $iv);
        System.out.println("duration:" + System.currentTimeMillis());
end

测试类

package org.example;

import org.kie.api.KieServices;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.conf.TimedRuleExecutionOption;
import org.kie.api.runtime.rule.AgendaFilter;

/**
 * @author Administrator
 */
public class DroolsApp {
    public static void main(String[] args) throws InterruptedException {

        //KIE 服务
        KieServices services = KieServices.Factory.get();
        //KIE 容器
        KieContainer container = services.getKieClasspathContainer();
        //会话配置
        KieSessionConfiguration ksc = KieServices.Factory.get().newKieSessionConfiguration();
        ksc.setOption( TimedRuleExecutionOption.YES );

        String ksName = "my_ks";

        //获取新会话
        KieSession session = container.newKieSession(ksName,ksc);

        //定义全局变量
        session.setGlobal("num",0);
        session.insert(1);
        //过滤规则
        AgendaFilter filter = match -> {
            Rule rule = match.getRule();
            if (rule.getName().startsWith("my-duration")){
                return true;
            }
            return false;
        };

        System.out.println("class:" + System.currentTimeMillis());
        //激活
        session.fireAllRules(filter);

        //销毁
        //session.destroy();

    }
}

执行结果

在这里插入图片描述

7.定时器测试

新建包 rules\time 并定义规则

package rules.time

rule "timer-1"
    enabled false
    timer ( cron:0/10 * * * * ? )
    when
    then
        System.out.println("timer-1:" + System.currentTimeMillis());
end

rule "timer-2"
    enabled false
    timer ( int: 10s 3s )
    when
    then
        System.out.println("timer-2:" + System.currentTimeMillis());
end

测试类

package org.example;

import org.kie.api.KieServices;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.conf.TimedRuleExecutionOption;
import org.kie.api.runtime.rule.AgendaFilter;

/**
 * @author Administrator
 */
public class DroolsApp {
    public static void main(String[] args) throws InterruptedException {

        //KIE 服务
        KieServices services = KieServices.Factory.get();
        //KIE 容器
        KieContainer container = services.getKieClasspathContainer();
        //会话配置
        KieSessionConfiguration ksc = KieServices.Factory.get().newKieSessionConfiguration();
        ksc.setOption( TimedRuleExecutionOption.YES );

        String ksName = "my_ks";

        //获取新会话
        KieSession session = container.newKieSession(ksName,ksc);

        //过滤规则
        AgendaFilter filter = match -> {
            Rule rule = match.getRule();
            if (rule.getName().startsWith("timer")){
                return true;
            }
            return false;
        };

        System.out.println("class  :" + System.currentTimeMillis());

        //激活
        session.fireAllRules(filter);

        //销毁
        //session.destroy();

    }
}

timer-1 测试结果 每 10 秒

在这里插入图片描述

timer-2 测试结果 延迟 10 秒后每 3 秒

在这里插入图片描述

文章来源:https://blog.csdn.net/weixin_42176639/article/details/134959946
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。