规则引擎

规则引擎概述

  • 规则引擎,全称为业务规则管理系统,英文名为BRMS

  • 就是把业务里经常变动的代码给抽离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策

  • 主流产品:drools、VisualRules、iLog

drools

概述

drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。

drools官网地址:https://drools.org/

drools源码下载地址:https://github.com/kiegroup/drools

基础使用

创建spring boot工程

引入drools依赖

<?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</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <java.version>17</java.version>
        <drools.version>8.41.0.Final</drools.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-core</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-decisiontables</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-mvel</artifactId>
            <version>${drools.version}</version>
        </dependency>
    </dependencies>

</project>

创建drools配置类

@Configuration
public class DroolsConfig {

    private static final KieServices kieServices = KieServices.Factory.get();
    //制定规则文件的路径
    private static final String RULES_CUSTOMER_RULES_DRL = "rules/order.drl";

    @Bean
    public KieContainer kieContainer() {
        //获得Kie容器对象
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));

        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
        kieBuilder.buildAll();

        KieModule kieModule = kieBuilder.getKieModule();
        KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());

        return kieContainer;
    }
}
  • 定义了一个 KieContainerBeanKieContainer用于通过加载应用程序的/resources文件夹下的规则文件来构建规则引擎。

  • 创建KieFileSystem实例并配置规则引擎并从应用程序的资源目录加载规则的 DRL 文件。

  • 使用KieBuilder实例来构建 drools 模块。我们可以使用KieSerive单例实例来创建 KieBuilder 实例。

  • 最后,使用 KieService 创建一个 KieContainer 并将其配置为 spring bean

创建实体类

public class Order {

    private double amount;
    private double score;

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}

创建规则文件

package com.hh.order;
import com.hh.bean.Order

//规则一:100元以下 不加分
rule "order_rule_1"
    when
        $order:Order(amount < 100)
    then
        $order.setScore(0);
        System.out.println("成功匹配到规则一:100元以下 不加分");
end

//规则二:100元 - 500元 加100分
rule "order_rule_2"
    when
        $order:Order(amount >= 100 && amount < 500)
    then
         $order.setScore(100);
         System.out.println("成功匹配到规则二:100元 - 500元 加100分");
end

//规则三:500元 - 1000元 加500分
rule "order_rule_3"
    when
        $order:Order(amount >= 500 && amount < 1000)
    then
         $order.setScore(500);
         System.out.println("成功匹配到规则三:500元 - 1000元 加500分");
end

//规则四:1000元以上 加1000分
rule "order_rule_4"
    when
        $order:Order(amount >= 1000)
    then
         $order.setScore(1000);
         System.out.println("成功匹配到规则四:1000元以上 加1000分");
end

测试

@SpringBootTest
public class DroolsDemosApplicationTests {

    @Autowired
    private KieContainer kieContainer;

    @Test
    public void test() {
        //从Kie容器对象中获取会话对象
        KieSession session = kieContainer.newKieSession();

        //Fact对象,事实对象
        Order order = new Order();
        order.setAmount(1300);

        //将Order对象插入到工作内存中
        session.insert(order);

        //激活规则,由Drools框架自动进行规则匹配,如果规则匹配成功,则执行当前规则
        session.fireAllRules();

        //关闭会话
        session.dispose();

        System.out.println("订单金额:" + order.getAmount() + ",添加积分:" + order.getScore());
    }
}
  • 使用drools规则引擎主要工作就是编写规则文件,在规则文件中定义跟业务相关的业务规则。规则定义好后就需要调用drools提供的API将数据提供给规则引擎进行规则模式匹配,规则引擎会执行匹配成功的规则并将计算的结果返回
  • 使用规则引擎时业务规则可以做到动态管理,可以做到不重启服务情况下做调整

规则引擎构成

drools规则引擎由以下三部分构成:

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

其中Inference Engine(推理引擎)又包括:

  • Pattern Matcher(匹配器)     // 具体匹配哪一个规则,由这个完成
  • Agenda(议程)
  • Execution Engine(执行引擎)

Working Memory:工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,所以我们开发的应用程序只需要将我们的数据插入到Working Memory中即可,例如上面调用kieSession.insert(order)就是将order对象插入到了工作内存中。

Fact:事实,是指在drools 规则应用当中,将一个普通的JavaBean插入到Working Memory后的对象就是Fact对象,例如本案例中的Order对象就属于Fact对象。

Rule Base:规则库,我们在规则文件中定义的规则都会被加载到规则库中。

Pattern Matcher:匹配器,将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,匹配成功的规则将被激活并放入Agenda(议程)中。

Agenda:议程,用于存放通过匹配器进行模式匹配后被激活的规则。

Execution Engine:执行引擎,执行Agenda中被激活的规则。

Drools 基础语法

基础语法

关键字描述
package包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用
import用于导入类或者静态方法
global全局变量
function自定义函数
query查询
rule end规则体
Drools 支持的规则文件除了drl格式,还有Excel文件类型

规则体语法

rule "ruleName"
    attributes
    when
        LHS 
    then
        RHS
end

rule:关键字,表示规则开始,参数为规则的唯一名称。

attributes:规则属性,是rule与when之间的参数,为可选项。

when:关键字,后面跟规则的条件部分。

LHS(Left Hand Side):是规则的条件部分的通用名称。它由零个或多个条件元素组成。如果LHS为空,则它将被视为始终为true的条件元素。 (左手边)

then:关键字,后面跟规则的结果部分。

RHS(Right Hand Side):是规则的后果或行动部分的通用名称。 (右手边)

end:关键字,表示一个规则结束。

Pattern 模式匹配

Drools中的匹配器可以将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,条件就叫做Pattern

pattern的语法结构为:绑定变量名:Object(Field约束)

例如:

//规则一:100元以下 不加分
rule "order_rule_1"
    when
        $order:Order(amount < 100)
    then
        $order.setScore(0);
        System.out.println("成功匹配到规则一:100元以下 不加分");
end

通过上面的例子可以知道,匹配的条件为:

1、$order对应对象是一个Order这种类型的Fact对象—–类型约束

2、Fact对象的amount属性值必须小于100——属性约束

以上条件必须同时满足当前规则才有可能被激活。

比较运算符

符号说明
contains检查一个Fact对象的某个属性值是否包含一个指定的对象值
not contains检查一个Fact对象的某个属性值是否不包含一个指定的对象值
memberOf判断一个Fact对象的某个属性是否在一个或多个集合中
not memberOf判断一个Fact对象的某个属性是否不在一个或多个集合中
matches判断一个Fact对象的属性是否与提供的标准的Java正则表达式进行匹配
not matches判断一个Fact对象的属性是否不与提供的标准的Java正则表达式进行匹配

Drools内置方法

规则文件的RHS部分的主要作用是通过插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,操作完成后规则引擎会重新进行相关规则的匹配, 原来没有匹配成功的规则在我们修改数据完成后有可能就会匹配成功了。

修改 update

update方法的作用是更新工作内存中的数据,并让相关的规则重新匹配。**(要避免死循环)

**要注意 ;

以上面的order为例 参数:

order.setAmount(30);

规则:

rule "order_rule"
	when
		$order:Order(amount < 100)
	then
		$order.setAmount(150);
		update($order)
		System.out.println("成功匹配到规则一:100元以下 不加分");
end

rule "order_rule2"
	when 
		$order:Order(amount >= 100 && amount <= 500)
	then
		$order.setScore(100);
		System.out.println("成功匹配到规则二:100元 - 500元 加100分");
end

添加 insert

insert方法的作用是向工作内存中插入数据,并让相关的规则重新匹配。

rule "order_rule"
	when
		$order:Order(amount < 100)
	then
		Order order = new Order();
		order.setAmount(30);
		insert($order)
		System.out.println("成功匹配到规则一:100元以下 不加分");
end

rule "order_rule2"
	when 
		$order:Order(amount >= 100 && amount <= 500)
	then
		$order.setScore(100);
		System.out.println("成功匹配到规则二:100元 - 500元 加100分");
end

删除 retract

retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。

rule "order_rule_1"
    when
        $order:Order(amout < 100)
    then
        retract($order)      //retract方法的作用是删除工作内存中的Fact对象,会导致相关规则重新匹配
        System.out.println("成功匹配到规则一:100元以下 不加分");
end

规则属性

属性名说明
salience指定规则执行优先级
dialect指定规则使用的语言类型,取值为java和mvel
enabled指定规则是否启用
date-effective指定规则生效时间
date-expires指定规则失效时间
activation-group激活分组,具有相同分组名称的规则只能有一个规则触发
agenda-group议程分组,只有获取焦点的组中的规则才有可能触发
timer定时器,指定规则触发的时间
auto-focus自动获取焦点,一般结合agenda-group一起使用
no-loop防止死循环

salience

  • salience属性用于指定规则的执行优先级,取值类型为Integer数值越大越优先执行。每个规则都有一个默认的执行顺序,如果不设置salience属性,规则体的执行顺序为由上到下
package com.order

rule "rule_1"
	salience 9
    when
        eval(true)
    then
        System.out.println("规则rule_1触发");
end
    
rule "rule_2"
	salience 10
    when
        eval(true)
    then
        System.out.println("规则rule_2触发");
end

rule "rule_3"
	salience 7
    when
        eval(true)
    then
        System.out.println("规则rule_3触发");
end

no-loop

  • no-loop属性用于防止死循环,当规则通过update之类的函数修改了Fact对象时,可能使当前规则再次被激活从而导致死循环。取值类型为Boolean,默认值为false

  • 引擎默认不会重复触发同一规则​ ​例如这个例子,修改了与规则无关的值然后再次进入相同的规则,规则不会再次触发

rule "order_rule_1"
    when
        $order:Order(amout < 100)
    then
        $order.setScore(0);
        update($order)
        System.out.println("成功匹配到规则一:100元以下 不加分");
end

想要实现死循环应该修改与规则有关的值,如下

rule "order_rule_1"
    when
        $order:Order(amount < 100)
    then
        $order.setAmount(0);
        update($order)
        System.out.println("成功匹配到规则一:100元以下 不加分");
end
  • 避免死循环:
rule "order_rule_1"
    no-loop true         //防止陷入死循环
    when
        $order:Order(amount < 100)
    then
        $order.setAmount(0);
        update($order)
        System.out.println("成功匹配到规则一:100元以下 不加分");
end

高级语法

关键字描述
package包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用
import用于导入类或者静态方法
global全局变量
function自定义函数
query查询
rule end规则体

global全局变量

  • global关键字用于在规则文件中定义全局变量,它可以让应用程序的对象在规则文件中能够被访问。可以用来为规则文件提供数据或服务。

  • 语法结构为:global 对象类型 对象名称

  • 注意:

    • 如果对象类型为包装类型时,在一个规则中改变了global的值,那么只针对当前规则有效,对其他规则中的global不会有影响。可以理解为它是当前规则代码中的global副本,规则内部修改不会影响全局的使用。
    • 如果对象类型为集合类型或JavaBean时,在一个规则中改变了global的值,对java代码和所有规则都有效。

规则文件:

package com.hh.order
import com.hh.bean.Order

global com.hh.bean.Order order_global;

//规则一:100元以下 不加分
rule "order_rule_1"
    no-loop true         //防止陷入死循环
    when
        $order:Order(amount < 100)
    then
        order_global.setScore(10);
        update($order)
        System.out.println("成功匹配到规则一:100元以下 不加分");
end

测试:

@Test
public void test1(){
    //从Kie容器对象中获取会话对象
    KieSession session = kieContainer.newKieSession();

    //Fact对象,事实对象
    Order order = new Order();
    order.setAmout(30);

    //全局变量
    Order order2 = new Order();
    session.setGlobal("order_global", order2);

    //将Order对象插入到工作内存中
    session.insert(order);

    //激活规则,由Drools框架自动进行规则匹配,如果规则匹配成功,则执行当前规则
    session.fireAllRules();
    //关闭会话
    session.dispose();

    System.out.println("订单金额:" + order.getAmout());
    System.out.println("添加积分:" + order2.getScore());
}
Posted on:
April 30, 2025
Length:
4 minute read, 697 words
Tags:
project
See Also:
CompletableFuture异步编排
MongoDB
分布式事物锁