前言:规则引擎的用途,可以通过修改规则配置,从而动态调整业务规则,搭配可视化工具,适合于业务人员随时调整规则。相对于 HardCode , 虽然需要一定学习成本,但具备以下优势
适用场景
Drools 是 JBoss 旗下 KIE (Knowledge is everything)体系的一个子产品,基于 Java 语言开发,用于进行规则解析与执行。
默认配置文件:src/main/resources/META-INF/kmodule.xml
kbase 标签
属性 | 含义 |
---|---|
name | kbase 名称,全局唯一,不允许重复,可以理解为工作空间或命名空间 |
includes | 包含,用于将多个kbase封装到一起,通过【,】分割 |
packages | 包名,即规则文件的位置,通过【,】分割可配置多个 |
default | 是否为默认命名空间 |
equalsBehavior | 相等的判断逻辑,用代码说明就是 == 和 equals 的区别,即 identity 和 equality |
eventProcessingMode | 事件模式,stream 模式允许进行时间推理,cloud为普通fact |
declarativeAgenda | disable或enable 用于控制规则间逻辑关系 |
ksession 标签
属性 | 含义 |
---|---|
name | ksession 名称,全局唯一,不允许重复 |
type | stateful 有状态,对 working memory 内数据多次处理 stateless 无状态 |
default | 是否为默认会话 |
clockType | 时钟类型,realtime 系统时钟 pseudo 伪时钟,可用于单元测试 |
beliefSystem | 信仰系统,用于控制资源访问和冲突 |
规则配置文件位置: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 | 正则不匹配 |
Drools 规则引擎构成
其中推理引擎又包括:
概念 | 含义 |
---|---|
KieServices | KIE的顶层抽象,用于创建、管理和获取KieContainer、KieSession等 |
KieContainer | kbase 实例化后的一个容器,一组规则实例 |
KieSession | 用于与 kbase 实例交互的一个会话 |
kieModules | 通过 xml 配置进行 kbase、ksession 的声明 |
KieRepository | 用于存放 KieModule 的单例对象,即仓库 |
KieProject | 初始化 KieModule,并将其存放到KieRepository仓库中,然后 KieContainer 可以通过 KieProject 来查找 KieModule ,并根据这些信息构造KieBase 和 KieSession |
ClasspathKieProject | KieProject的一个具体实现,从根目录加载kmodule.xml配置,从而初始化一个 kiemodule |
Working Memory | 工作内存,将待处理数据插入进去,drools 进行处理 |
Fact | 一个普通的 JavaBean 插入到 Working Memory 后,drools 称之为Fact 对象 |
Rule Base | 规则库 |
Pattern Matcher | 将规则库规则与工作内存中事实对象进行匹配,成功则将规则放到议程 |
Agenda | 议程,存放上一步通过匹配器进行模式匹配后被激活的规则 |
Execution Engine | 执行议程内激活的规则 |
不同版本差异较大,本示例对应版本如下
环境 | 版本 |
---|---|
JDK | jdk-21.0.1 |
Drools | 9.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>
关键字 | 含义 | 示例 |
---|---|---|
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 或 MVEL | dialect “JAVA” |
新建 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();
}
}
结果
将上面的 salience_2.drl 修改为
package rules.salience
rule "salience-2"
enabled false
salience 2
when
then
System.out.println(2);
end
重新执行结果,规则 2 已不可用
将 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 开始失效
定义一个测试用的对象
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 不允许循环结果如下
在这里插入代码片
新建包 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
新建包 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();
}
}
执行结果
新建包 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 秒