Drools

Drools简介

Drools是Java编写的一款开源规则引擎,实现了Rete算法对所编写的规则求值,支持声明方式表达业务逻辑。使用DSL(Domain Specific Language)语言来编写业务规则,使得规则通俗易懂,便于学习理解。支持Java代码直接嵌入到规则文件中。

Drools主要分为两个部分:一是Drools规则,二是Drools规则的解释执行。规则的编译与运行要通过Drools 提供的相关API 来实现。而这些API 总体上游可分为三类:规则编译、规则收集和规则的执行。

Drools是业务规则管理系统(BRMS)解决方案,涉及以下项目:

Drools Workbench:业务规则管理系统 Drools Expert:业务规则引擎 Drools Fusion:事件处理 jBPM:工作流引擎 OptaPlanner:规划引擎

Drools API

kmodule.xml

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <kbase name="rules" packages="com.rules">
        <ksession name="ksession-rule"/>
    </kbase>
</kmodule>
1
2
3
4
5
6

package com.rules.model;

public class Product {

    public static final String DIAMOND = "DIAMOND"; // 钻石

    public static final String GOLD = "GOLD"; // 黄金

    private String type;

    private int discount;
    // 省略getter/setter方法
}

package com.rules

import com.ruls.model.Product

rule Offer4Diamond
    when
        productObject : Product(type == Product.DIAMOND)
    then
        productObject.setDiscount(15);
    end
rule Offer4Gold
    when
        productObject: Product(type == Product.GOLD)
    then
        productObject.setDiscount(25);
    end

@Test
    public void testRules() {
        // 构建KieServices
        KieServices ks = KieServices.Factory.get();
        KieContainer kieContainer = ks.getKieClasspathContainer();
        // 获取kmodule.xml中配置中名称为ksession-rule的session,默认为有状态的。
        KieSession kSession = kieContainer.newKieSession("ksession-rule");

        Product product = new Product();
        product.setType(Product.GOLD);

        kSession.insert(product);
        int count = kSession.fireAllRules();
        System.out.println("命中了" + count + "条规则!");
        System.out.println("商品" +product.getType() + "的商品折扣为" + product.getDiscount() + "%。");

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  • Kmodule中可以包含一个到多个kbase,分别对应drl的规则文件。
  • Kbase需要一个唯一的name,可以取任意字符串。
  • packages为drl文件所在resource目录下的路径。注意区分drl文件中的package与此处的package不一定相同。多个包用逗号分隔。默认情况下会扫描resources目录下所有(包含子目录)规则文件。
  • kbase的default属性,标示当前KieBase是不是默认的,如果是默认的则不用名称就可以查找到该KieBase,但每个module最多只能有一个默认KieBase。
  • kbase下面可以有一个或多个ksession,ksession的name属性必须设置,且必须唯一。

标准规则文件的结构

从架构角度来讲,一般将同一业务的规则放置在同一规则文件,也可以根据不同类型处理操作放置在不同规则文件当中。不建议将所有的规则放置与一个规则文件当中。分开放置,当规则变动时不至于影响到不相干的业务。读取构建规则的成本业务会相应减少。

标准规则文件的结构如下:

package package-name

imports

globals

functions

queries

rules
1
2
3
4
5
6
7
8
9
10
11

package:在一个规则文件当中package是必须的,而且必须放置在文件的第一行。package 的名字是随意的,不必必须对应物理路径,这里跟java的package的概念不同,只是逻辑上的区分,但建议与文件路径一致。同一的package下定义的function和query等可以直接使用。 比如,上面实例中package的定义:

package com.rules
1

import:导入规则文件需要的外部变量,使用方法跟java相同。像java的是import一样,还可以导入类中的某一个可访问的静态方法。(特别注意的是,某些教程中提示import引入静态方法是不同于java的一方面,可能是作者没有用过java的静态方法引入。)另外,目前针对Drools7版本,static和function关键字的效果是一样的。

import static com.secbro.drools.utils.DroolsStringUtils.isEmpty;
import function com.secbro.drools.utils.DroolsStringUtils.isEmpty;
1
2

rules:定义一个条规则。rule “ruleName”。一条规则包含三部分:属性部分、条件部分和结果部分。rule规则以rule开头,以end结尾。

属性部分:定义当前规则执行的一些属性等,比如是否可被重复执行、过期时间、生效时间等。

条件部分,简称LHS,即Left Hand Side。定义当前规则的条件,处于when和then之间。如when Message();判断当前workingMemory中是否存在Message对象。LHS中,可包含0~n个条件,如果没有条件,默认为eval(true),也就是始终返回 true。

结果部分,简称RHS,即Right Hand Side,处于then和end之间,用于处理满足条件之后的业务逻辑。可以使用LHS部分定义的变量名、设置的全局变量、或者是直接编写Java 代码。

RHS部分可以直接编写Java代码,但不建议在代码当中有条件判断,如果需要条件判断,那么需要重新考虑将其放在LHS部分,否则就违背了使用规则的初衷。

RHS部分,提供了一些对当前Working Memory实现快速操作的宏函数或对象,比如 insert/insertLogical、update/modify和retract等。利用这些函数可以实现对当前Working Memory中的Fact对象进行新增、修改或删除操作;如果还要使用Drools提供的其它方法,可以使用另一个外宏对象drools,通过该对象可以使用更多的方法;同时Drools 还提供了一个名为kcontext的宏对象,可以通过该对象直接访问当前Working Memory的 KnowledgeRuntime。

标准规则的结构示例:

rule "name"
    attributes
    when
        LHS
    then 
        RHS
    end
1
2
3
4
5
6
7

LHS为空示例:

rule "name"
when 
then 
end
1
2
3
4

规则的attributes

no-loop

定义当前的规则是否不允许多次循环执行,默认是 false,也就是当前的规则只要满足条件,可以无限次执行。

lock-on-active

当在规则上使用ruleflow-group属性或agenda-group属性的时候,将lock-on-active 属性的值设置为true,可避免因某些Fact对象被修改而使已经执行过的规则再次被激活执行。可以看出该属性与no-loop属性有相似之处,no-loop属性是为了避免Fact被修改或调用了insert、retract、update之类的方法而导致规则再次激活执行,这里的lock-on-active 属性起同样的作用,lock-on-active是no-loop的增强版属性,它主要作用在使用ruleflow-group属性或agenda-group属性的时候。lock-on-active属性默认值为false。与no-loop不同的是lock-on-active可以避免其他规则修改FACT对象导致规则的重新执行。

ruleflow-group

在使用规则流的时候要用到ruleflow-group属性,该属性的值为一个字符串,作用是将规则划分为一个个的组,然后在规则流当中通过使用ruleflow-group属性的值,从而使用对应的规则。该属性会通过流程的走向确定要执行哪一条规则。在规则流中有具体的说明。

salience

用来设置规则执行的优先级,salience属性的值是一个数字,数字越大执行优先级越高,同时它的值可以是一个负数。默认情况下,规则的salience默认值为0。如果不设置规则的salience属性,那么执行顺序是随机的。

agenda-group

规则的调用与执行是通过StatelessKieSession或KieSession来实现的,一般的顺序是创建一个StatelessKieSession或KieSession,将各种经过编译的规则添加到session当中,然后将规则当中可能用到的Global对象和Fact对象插入到Session当中,最后调用fireAllRules 方法来触发、执行规则。

在没有调用fireAllRules方法之前,所有的规则及插入的Fact对象都存放在一个Agenda表的对象当中,这个Agenda表中每一个规则及与其匹配相关业务数据叫做Activation,在调用fireAllRules方法后,这些Activation会依次执行,执行顺序在没有设置相关控制顺序属性时(比如salience属性),它的执行顺序是随机的。

Agenda Group是用来在Agenda基础上对规则进行再次分组,可通过为规则添加agenda-group属性来实现。agenda-group属性的值是一个字符串,通过这个字符串,可以将规则分为若干个Agenda Group。引擎在调用设置了agenda-group属性的规则时需要显示的指定某个Agenda Group得到Focus(焦点),否则将不执行该Agenda Group当中的规则。

auto-focus

activation-group

dialect

该属性用来定义规则(LHS、RHS)当中要使用的语言类型,可选值为“java”或“mvel”。默认情况下使用java语言。当在包级别指定方言时,这个属性可以在具体的规则中覆盖掉包级别的指定。

date-effective

该属性是用来控制规则只有在到达指定时间后才会触发。在规则运行时,引擎会拿当前操作系统的时间与date-effective设置的时间值进行比对,只有当系统时间大于等于date-effective设置的时间值时,规则才会触发执行,否则执行将不执行。在没有设置该属性的情况下,规则随时可以触发

date-expires

此属性与date-effective的作用相反,用来设置规则的过期时间。时间格式可完全参考date-effective的时间格式。引擎在执行规则时会检查属性是否设置,如果设置则比较当前系统时间与设置时间,如果设置时间大于系统时间,则执行规则,否则不执行。

enabled

设置规则是否可用。true:表示该规则可用;false:表示该规则不可用。