Drools 规则引擎



前言

随着互联网的不断发展,有很多平台存在比较复杂的业务规则并且这些规则会随着需求频繁变换,这时候就需要找到一个可以将活动规则和代码解耦的技术,那就是规则引擎。


一、规则引擎是什么?

规则引擎:全称为业务规则管理系统,英文名为BRMS。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或 开发者在需要时进行配置、管理。需要注意的是规则引擎并不是一个具体的技术框架,而是指的一类系统,即业务规则管理系统。

java开源的规则引擎有:Drools、Easy Rules、Mandarax、IBM ILOG。使用最为广泛并且开源的是Drools。

主要应用场景:

  • 风控系统-------风险贷款、风险评估
  • 反欺诈项目-----银行贷款、征信验证
  • 决策平台-------财务计算
  • 电商平台------满减、打折、加价购

二、Drools 简介

Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,将规则与业务代码解耦,规则以脚本的形式存储在一个文件中,使规则的变化不需要修改代码,重新启动机器即可在线上环境中生效。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。

官网:http://www.drools.org/#
官方文档:http://www.drools.org/learn/documentation.html

1.引入规则引擎前后程序架构:

引入前:
在这里插入图片描述
引入后:
在这里插入图片描述

2.Drools API 开发步骤:

在这里插入图片描述


三、Drools 快速入门

1.使用项目文件作为规则引擎

此章节引用的华为云开发者联盟文章中的实例,原文链接如下。 第二章使用数据库作为规则引擎为原创

原文链接:https://huaweicloud.csdn.net/63356f78d3efff3090b56d8f.html

电商平台促销积分规则

100元以下, 不加分 
100-500元 加100500-1000元 加5001000元 以上 加1000

开发实现
第一步:创建Maven项目,添加Drools 规则引擎依赖。

 <!--添加规则引擎依赖-->
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>7.73.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-mvel</artifactId>
            <version>7.73.0.Final</version>
        </dependency>
        <!--添加单元测试工具类-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

第二步:根据drools要求创建resources/META-INF/kmodule.xml 配置文件

需要有一个配置文件告诉代码规则文件drl 在哪里,在drools中这个文件就是kmodule.xml。

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
    name:指定kbase 名称,可以是任意但必须唯一
    packages:指定规则文件存放目录,依据实际情况进行填写
    default:指定当前的kbase 是否为默认
    -->
    <kbase name="rules" packages="rules" default="true">
        <!--
        name:指定ksession名称,可以是任意但必须唯一
        default:指定ksession 是否为默认
        -->
        <ksession name="ksession-rules" default="true"/>
    </kbase>
</kmodule>

第三步:创建业务实体对象

package com.zzg.model;

public class Order implements java.io.Serializable {

    private int amount; //订单金额

    private int score; //订单积分

    @Override
    public String toString() {
        return "Order{" +
                "amount=" + amount +
                ", score=" + score +
                '}';
    }

    public int getAmount() {
        return amount;
    }

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

    public int getScore() {
        return score;
    }

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

第四步:创建规则文件,在resources/rules/score-rules.drl

package rules;

import com.zzg.model.Order;

// 100 元以下不加分
rule "score_1"
when
    $order:Order(amount<100);
then
    $order.setScore(0);
    System.out.println("触发100元以下,不加积分");
end
// 100-500 元, 加100积分
rule "score_2"
when
    $order:Order(amount>100 && amount <= 500);
then
    $order.setScore(100);
    System.out.println("触发100-500 元, 加100积分");
end
// 500-10000 元, 加500积分
rule "score_3"
when
    $order:Order(amount>500 && amount <= 1000);
then
    $order.setScore(500);
    System.out.println("触发500-1000 元, 加500积分");
end
// 大于1000 元, 加1000积分
rule "score_4"
when
    $order:Order(amount>1000);
then
    $order.setScore(1000);
    System.out.println("触发大于1000 元, 加1000积分");
end

第五步:单元测试

package com.zzg;

import com.zzg.model.Order;
import org.junit.Test;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

public class TestDrools {
    @Test
    public void test1() {
        // 第一步
        KieServices kieServices = KieServices.Factory.get();
        // 第二步
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        // 第三步
        KieSession kieSession = kieContainer.newKieSession();
        // 业务对象
        Order order = new Order();
        order.setAmount(100);
        // 第四步
        kieSession.insert(order);
        // 第五步:执行规则引擎
        kieSession.fireAllRules();
        // 第六步:关闭session
        kieSession.dispose();

        System.out.println("指定规则引擎后的结果:" + order.getScore());

    }
}

效果截图:

23:01:01.112 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: rules
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED
指定规则引擎后的结果:0

2.使用数据库存储规则引擎

电商平台促销积分规则

100元以下, 不加分 
100-500元 加100500-1000元 加5001000元 以上 加1000

开发实现
第一步:创建Maven项目,添加Drools 规则引擎依赖。

 <!--添加规则引擎依赖-->
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>7.73.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-api</artifactId>
            <version>7.73.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-core</artifactId>
            <version>7.73.0.Final</version>
        </dependency>
        <!--添加单元测试工具类-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

第二步:根据drools要求创建resources/META-INF/kmodule.xml 配置文件 因在数据库读取规则,经测试发现不写kmodule.xml 配置文件 也可以

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
    name:指定kbase 名称,可以是任意但必须唯一
    packages:指定规则文件存放目录,依据实际情况进行填写
    default:指定当前的kbase 是否为默认
    -->
    <kbase name="rules" packages="rules" default="true">
        <!--
        name:指定ksession名称,可以是任意但必须唯一
        default:指定ksession 是否为默认
        -->
        <ksession name="ksession-rules" default="true"/>
    </kbase>
</kmodule>

第三步:创建业务实体对象

package com.zzg.model;

public class Order implements java.io.Serializable {

    private int amount; //订单金额

    private int score; //订单积分

    @Override
    public String toString() {
        return "Order{" +
                "amount=" + amount +
                ", score=" + score +
                '}';
    }

    public int getAmount() {
        return amount;
    }

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

    public int getScore() {
        return score;
    }

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

第四步:创建规则文件,使用insert into 语句插入数据库(oracle数据库

数据库字段为 serialNo(主键)、rule(规则内容) rule类型为CLOB
这里我使用了另一种写法,如果使用上边那种写法也可以,上边的写法还规范!!!

DECLARE
	V_LANG CLOB :=
	'package rules
	import com.zzg.model.Order;
	
	// 100 元以下不加分
	rule "score_1"
	when
		context:Order(amount<100)
	then
	    context.setScore(0);
	    System.out.println("触发100元以下,不加积分");
	end
	// 100-500 元, 加100积分
	rule "score_2"
	when
	    context:Order(amount>100 &&& amount <= 500);
	then
	    context.setScore(100);
	    System.out.println("触发100-500 元, 加100积分");
	end
	// 500-10000 元, 加500积分
	rule "score_3"
	when
	    context:Order(amount>500 &&& amount <= 1000);
	then
	    context.setScore(500);
	    System.out.println("触发500-1000 元, 加500积分");
	end
	// 大于1000 元, 加1000积分
	rule "score_4"
	when
	    context:Order(amount>1000);
	then
	    context.setScore(1000);
	    System.out.println("触发大于1000 元, 加1000积分");
	end';
BEGIN
	insert into	t_rule(serialNo, rule) values (1, V_LANG);
END;

这里我没测试,如果直接复制可能有错,上边的包引得可能有问题,因为代码不在本地电脑。抱歉。
第五步:单元测试

package com.zzg;

import com.zzg.model.Order;
import org.junit.Test;
import org.kie.io.ResourceType;
import org.kie.api.KieServices;
import org.kie.api.runtime.KieSession;
import org.kie.internal.utils.KieHelper;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.sql.Clob;
import java.sql.SQLException;

public class TestDrools {
    @Test
    public void test1() {
        // 第一步 读取数据库中规则 这里大家按照自己项目持久层框架写就OK 
        // 实体类中 rule 的类型为 private Clob rule;
		RuleEntity ruleEntity = ruleDao.getRuleEntity();
		// 第二步 把clob类型转换为String类型
		String myRule = clobToString(ruleEntity.getRule());
		// 第三步 创建规则对象 将String转换成DRL规则
		KieHelper kieHelper = new KieHelper();
		kieHelper.addContent(myRule, ResourceType.DRL);
		// 第四步 会话对象,用于和规则引擎交互
		KieSession kieSession = kieHelper.build().newKieSession();
        // 业务对象
        Order order = new Order();
        order.setAmount(100);
        // 第四步
        kieSession.insert(order);
        // 第五步:执行规则引擎
        kieSession.fireAllRules();
        // 第六步:关闭session
        kieSession.dispose();

        System.out.println("指定规则引擎后的结果:" + order.getScore());

    }
	
	private String clobToString(Clob clob) throws Exception{
		String str = "";
		//得到流
		Reader rd = clob.getCharacterStream();
		BufferedReader br = new BufferedReader(rd );
		String s = br.readLine();
		StringBuilder sb= new StringBuilder();
        while(s != null) {
          sb.append(s);
          s = br.readLine();
        }
        str = sb.toString();
        return str;
	}
}

附加
这里写一下对此字段的增删改查语法
原文链接:https://huaweicloud.csdn.net/63355c9ad3efff3090b5418d.html?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Eactivity-3-120262609-blog-126691379.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Eactivity-3-120262609-blog-126691379.pc_relevant_recovery_v2&utm_relevant_index=6

--使用PL/SQL语法,采取绑定变量的方式解决,而不是直接拼接SQL
DECLARE
    V_LANG CLOB := '待插入的海量字符串';
    V_UPDATE CLOB := '更新的海量字符串';
BEGIN
 INSERT INTO temp t VALUES ('Grand.Jon', 22, V_LANG);    --增加
 UPDATE temp t SET t.temp_clob = V_UPDATE WHERE rownum = 1; --修改
 SELECT t.NAME, dbms_lob.substr(t.temp_clob) FROM TEMP t;  --查询  将CLOB转成字符类型
 DELETE temp t WHERE rownum = 1;               --按列删除  
 COMMIT;
END;
/

效果截图:

23:01:01.112 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: rules
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now FIRING_ALL_RULES
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was FIRING_ALL_RULES is now HALTING
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was HALTING is now INACTIVE
23:01:01.144 [main] DEBUG org.drools.core.common.DefaultAgenda - State was INACTIVE is now DISPOSED
指定规则引擎后的结果:0

四、Drools 规则引擎构成及其核心类

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

  • Working Memory(工作内存)
  • Rules(规则库)
  • Inference Engine(推理引擎)
  • 其中Inference Engine(t推理引擎)又包含如下:

Pattern Match(匹配器)具体匹配那一个规则,由它来完成

  • Agenda(议程)
  • Execution Engine(执行引擎)
    在这里插入图片描述

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

Fact: 事实,是指在drools 规则应用当中,将一个普通的JavaBean插入到Working Memory后的对象就是Fact对象,例如本案例中的Order对象就属于Fact对象。Fact对象是我们的应用和规则引擎进行数据交互的桥梁或通道。

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

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

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


总结

以上就是今天要讲的内容,本文仅仅简单介绍了Drools 规则引擎,而Drools 提供了大量能使我们快速便捷地处理数据的函数和方法。大家可以看一下原文链接,里边讲解了一些关于Drools 基础语法等内容。

参考文章:
Drools 规则引擎一文读懂 : https://huaweicloud.csdn.net/63356f78d3efff3090b56d8f.html
【Drools】Drools使用入门(一)Drools上手教程(包括动态加载规则文件) : http://t.csdn.cn/jyfF0
ORACLE中CLOB介绍及使用:https://huaweicloud.csdn.net/63355c9ad3efff3090b5418d.html?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Eactivity-3-120262609-blog-126691379.pc_relevant_recovery_v2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Eactivity-3-120262609-blog-126691379.pc_relevant_recovery_v2&utm_relevant_index=6

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐