规则引擎drools入门案例

规则引擎


相关介绍
  • 规则引擎起源于基于规则的专家系统,而基于规则的专家系统又是专家系统的其中一个分支。专家系统又属于人工智能的一个研究分支,它模仿人类的推理方式,使用试探性的方法进行推理,并使用人类能理解的术语解释和证明它的推理结论。

  • 利用规则引擎可以在业务系统中分离商业决策者的商业决策逻辑和应用开发者的技术决策,并把这些商业决策放在中心数据库或其他统一的地方,让它们能在运行时可以动态地管理和修改,从而为企业保持灵活性和竞争力提供有效的技术支持。

  • 在需求里面我们往往把约束,完整性,校验,分支流等都可以算到业务规则里面。在规则引擎里面谈的业务规则重点是谈当满足什么样的条件的时候,需要执行什么样的操作。因此一个完整的业务规则包括了条件和触发操作两部分内容。而引擎是事物内部的重要的运行机制,规则引擎即重点是解决规则如何描述,如何执行,如何监控等一系列问题。

规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。


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

规则引擎的优点
  • 声明式编程
    规则可以很容易地解决困难的问题,并得到解决方案的验证。与代码不同,规则以较不复杂的语言编写; 业务分析师可以轻松阅读和验证一套规则。

  • 逻辑和数据分离
    数据位于“域对象”中,业务逻辑位于“规则”中。根据项目的种类,这种分离是非常有利的。

  • 速度和可扩展性
    写入Drools的Rete OO算法已经是一个成熟的算法。在Drools的帮助下,您的应用程序变得非常可扩展。如果频繁更改请求,可以添加新规则,而无需修改现有规则。

  • 知识集中化
    通过使用规则,您创建一个可执行的知识库。这是商业政策的一个真理点。理想情况下,规则是可读的,它们也可以用作文档。

rete 算法

Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年发表的论文中所阐述的算法 , 该算法提供了专家系统的一个高效实现。自 Rete 算法提出以后 , 它就被用到一些大型的规则系统中 , 像 ILog、Jess、JBoss Rules 等都是基于 RETE 算法的规则引擎

Rete 在拉丁语中译为”net”,即网络。Rete 匹配算法是一种进行大量模式集合和大量对象集合间比较的高效方法,通过网络筛选的方法找出所有匹配各个模式的对象和规则。

其核心思想是将分离的匹配项根据内容动态构造匹配树,以达到显著降低计算量的效果。Rete 算法可以被分为两个部分:规则编译和规则执行。当Rete算法进行事实的断言时,包含三个阶段:匹配、选择和执行,称做 match-select-act cycle。

Drools 介绍

Drools 是一个基于Charles Forgy’s的RETE算法的,易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。业务分析师人员或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。

Drools 是用Java语言编写的开放源码规则引擎,使用Rete算法对所编写的规则求值。Drools允许使用声明方式表达业务逻辑。可以使用非XML的本地语言编写规则,从而便于学习和理解。并且,还可以将Java代码直接嵌入到规则文件中,这令Drools的学习更加吸引人。

Drools优点:

非常活跃的社区支持

易用

快速的执行速度

在 Java 开发人员中流行

与 Java Rule Engine API(JSR 94)兼容

Drools相关概念:

事实(Fact):对象之间及对象属性之间的关系

规则(rule):是由条件和结论构成的推理语句,一般表示为if…Then。一个规则的if部分称为LHS,then部分称为RHS。

模式(module):就是指IF语句的条件。这里IF条件可能是有几个更小的条件组成的大条件。模式就是指的不能在继续分割下去的最小的原子条件。

Drools通过事实、规则和模式相互组合来完成工作,Drools在开源规则引擎中使用率最广,但是在国内企业使用偏少,保险、支付行业使用稍多。


Example

  • 需求背景:公司双十一大促,活动规则是根据用户购买订单的金额给用户送相应的积分,购买的越多送的积分越多,用户可以使用积分来兑换相应的商品。
  • 用户购买的金额和对应送多少积分的规则如下:
    100元以下, 不加分
    100元-500元 加100分
    500元-1000元 加500分
    1000元 以上 加1000分
    注意:这样的业务规则会根据实际销售情况变化,比如买的人少了,可以加大积分额度。买的人好多可以适当地降低积分赠送~因此规则引擎派上了用场。

  • 导入maven依赖

         <properties>
                <drools.version>7.7.0.Final</drools.version>
         </properties>
         <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-templates</artifactId>
            <version>${drools.version}</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-api</artifactId>
            <version>${drools.version}</version>
        </dependency>
  • 配置xxx.drl规则文件和kmodule.xml文件
    规则文件 resources\rules\Point-rules.drl
package rules
import com.zpc.boot.Order
rule "zero"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout <= 100)
    then
        $s.setScore(0);
        update($s);
end
rule "add100"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 100 && amout <= 500)
    then
        $s.setScore(100);
        update($s);
end
rule "add500"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 500 && amout <= 1000)
    then
        $s.setScore(500);
        update($s);
end
rule "add1000"
    no-loop true
    lock-on-active true
    salience 1
    when
        $s : Order(amout > 1000)
    then
        $s.setScore(1000);
        update($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 属性的值是一个数字,数字越大执行优先级越高, 同时它的值可以是一个负数。默认情况下,规则的 salience 默认值为 0。如果不设置规则的 salience 属性,那么执行顺序是随机的。

when 条件语句,就是当到达什么条件的时候

then 根据条件的结果,来执行什么动作

end 规则结束

这个规则文件就是描述了,当符合什么条件的时候,应该去做什么事情,每当规则有变动的时候,我们只需要修改规则文件,然后重新加载即可生效。


这里需要有一个配置文件告诉代码规则文件drl在哪里,在Drools中这个文件就是kmodule.xml,放置到resources/META-INF目录下。

配置文件 resources/META-INF/kmodule.xml

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://jboss.org/kie/6.0.0/kmodule ">

    <kbase name="point-rulesKB" packages="rules">
        <ksession name="point-rulesKS"/>
    </kbase>
</kmodule>

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

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

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

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

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


  • 编写后端代码
    Order.java
public class Order {
    private String orderNum;
    private int amout;
    private int score;

    public  Order(String orderNum,int amout) {
        this.orderNum = orderNum;
        this.amout=amout;
    }

    public String getOrderNum() {
        return orderNum;
    }

    public void setOrderNum(String orderNum) {
        this.orderNum = orderNum;
    }

    public int getAmout() {
        return amout;
    }

    public void setAmout(int amout) {
        this.amout = amout;
    }

    public int getScore() {
        return score;
    }

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

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

DroolMain .java

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

import java.util.ArrayList;
import java.util.List;

public class DroolMain {
    public static final void main(final String[] args) throws Exception {
        KieServices ks = KieServices.Factory.get();
        KieContainer kc = ks.getKieClasspathContainer();
        List<Order> result = execute(kc);
        printResult(result);
    }

    private static void printResult(List<Order> results) {
        for (Order order : results) {
            System.out.println(order);
        }
    }

    public static List<Order> execute(KieContainer kc) throws Exception {
        KieSession ksession = kc.newKieSession("point-rulesKS");
        List<Order> orderList = getOrders();
        for (int i = 0; i < orderList.size(); i++) {
            Order o = orderList.get(i);
            ksession.insert(o);
            ksession.fireAllRules();
        }
        ksession.dispose();
        return orderList;
    }

    private static List<Order> getOrders() {
        List<Order> orderList = new ArrayList<Order>();
        orderList.add(new Order("xxx00001", 1200));
        orderList.add(new Order("xxx00002", 200));
        orderList.add(new Order("xxx00003", 99));
        orderList.add(new Order("xxx00004", 499));
        orderList.add(new Order("xxx00005", 999));
        return orderList;
    }
}
  • 运行代码
    Order{orderNum=’xxx00001’, amout=1200, score=1000}
    Order{orderNum=’xxx00002’, amout=200, score=100}
    Order{orderNum=’xxx00003’, amout=99, score=0}
    Order{orderNum=’xxx00004’, amout=499, score=100}
    Order{orderNum=’xxx00005’, amout=999, score=500}
    看到所有的订单被规则引擎处理后有了相应的score值~
    业务需求变更后只要修改对应的配置文件即可实现新的积分赠送策略!

猜你喜欢

转载自blog.csdn.net/zpcandzhj/article/details/80293387