初探Drools


背景

笔者最近在研究、使用Java规则引擎Drools,主要工作分两部分:1. Java EE环境下的Drools、Workbench的使用;2. Drools android环境的移植工作,本篇博客主要也是按照这两部分内容分别介绍的。


一、Drools基本概念

1.1 规则引擎

Drools是Java规则引擎,关于规则引擎的定义,笔者从百度百科摘录了如下定义

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

从规则的定义可知,一个完整的规则引擎应该包含:

  1. 规则定义
  2. 规则解析
  3. 决策执行

笔者调研过各种java规则引擎,大多数轻量级规则引擎包含上述的两种及全部。各类引擎也都是在一种或者多种领域个性化,扩充能力。
规则引擎引入前后架构对比

图1 规则引擎引入前后架构对比

1.2 Drools简介

Drools的定义,笔者也从百度百科摘录了定义,结果如下

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

Drools有如下特点:

  1. 完全开源现在仍在持续更新;
  2. 基于Charles Forgy的RETE算法的规则引擎的实现;
  3. 使用XML的、 节点表达If–Then句式,而里面可以嵌入上述语言的代码作为判断语句和执行语句。

Drools规则引擎架构

图2 引擎框架简图

二、Drools基本用法

2.1 规则文件

下面是一个标准的drl文件

// 图书优惠规则
package book.discount
import com.saic.entity.Order
import com.saic.entity.Constant

function void hello(int price){
   System.out.println(("cost down "+price));
}
declare Constant end

query "origin price larger than 100"
    $order : Order( originalPrice > 100 )
end

// 规则一:所购图书总价在100元以下的没有优惠
rule RULE_1
    when
        $order: Order(originalPrice < 100) // 匹配模式,到规则引擎中(工作内存)查找Order对象,命名为$order
        or $order: Order(realPrice >= 500)
    then
        $order.setRealPrice($order.getOriginalPrice());
        System.out.println("成功匹配到规则一,所购图书总价在100元以下无优惠");
end

从一个标准文件,可以看出:

  1. 一个规则文件包含:包名、引用、自定义函数、查询、对象申明、规则体等;
  2. 一个规则体包含规则名LHSRHS;
  3. 绑定变量名:[绑定变量名:Object(Field 约束)];
  4. 规则文件与Java可以实现双向调用;
  5. 有一点需要注意的是,运算符(+、-、*、/、%)与java优先级相同.
    drl文件引用的Java文件如下:
package com.saic.entity;

public class Order {
    private Double originalPrice; // 订单原始价格,即优惠前的价格
    private Double realPrice; // 订单真实价格,即优惠后的价格

    public Double getOriginalPrice() {
        return originalPrice;
    }

    public void setOriginalPrice(Double originalPrice) {
        this.originalPrice = originalPrice;
    }

    public Double getRealPrice() {
        return realPrice;
    }

    public void setRealPrice(Double realPrice) {
        this.realPrice = realPrice;
    }
}

2.2 配置文件

规则文件创建完成以后,需要配置xml文件方便引擎去识别、加载规则到内存中。对于初学者一般使用kmodule.xml配置引擎信息。Drools还支持一些高阶配置比如change-set。下面内容为一个kmodel.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">

    <!--
        name:指定kbase的名称,可以任意,但是需要唯一
        packages:指定规则文件的目录,需要根据实际情况填写,否则无法加载到规则文件
        default:指定当前kbase是否为默认
    -->
    <kbase name="myKbase1" packages="rules">
        <!--
            name:指定ksession的名称,可以任意,但需要唯一
            default:指定当前session是否为默认
        -->
        <ksession name="ksession-rule" default="true"/>
    </kbase>
</kmodule>
  1. kmodule.xml文件存放在src/main/resources/META-INFO文件夹下。一个kmodule.xml配置中可包含多个KieBase,每一个KieBase都有name属性,可以取任意字符串,但不能重名;
  2. KieBase有一个packages属性,其内容指向src/main/resources目录下文件夹的名称,规则引擎会根据packages定义的内容查找规则文件(文件夹下有子文件夹不会被加载);
  3. 每一个KieSession都有一个名称,名称可以是任意字符串,但不能重复。

2.3 数据驱动

规则创建完毕,规则被装载入内存后,需要数据驱动整个引擎工作,这个数据Drools命名为Fact。规则运算时需要用到应用系统中的数据,将这些对象设置到Fact对象中,然后将其插入Working Memory中,一个Fact通常是一个具有getter方法和setter方法的POJO对象,通过getter方法和setter方法可以方便的对Fact对象进行操作,所以可以通俗的把Fact对象理解为规则与应用系统数据交互的桥梁或者通道。下面事例代码描述了如何操作Fact对象,引擎匹配规则。

public class AccountTest {

    @Test
    public void testAccount() {
        KieServices kieServices = KieServices.Factory.get();
        // 获取Kie容器对象(默认容器对象
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        // 从Kie容器对象中获取会话对象(默认session对象
        KieSession kieSession = kieContainer.newKieSession();
        Account account = new Account();
        account.setBalance(88);
        kieSession.insert(account);
        int count = kieSession.fireAllRules();
        System.out.println("总共执行了" + count + "条规则");
        kieSession.dispose();
    }
}

三、Drools进阶用法

3.1 DSL语言

Drools领域语言又称作自然语言,它是业务人员通过dslr文件编写的规则文件,通过文字描述来实现业务规则。DSL是Drools提供的一种可通过非正规的规则语法编写的规则文件,使用DSL领域语言时,需要创建.dsl.dslr两个文件,核心是*.dsl文件。
DSL机制是允许定制conditional expressions(条件表达式LHS)和consequence actions(结果值RHS),也可以替换全局变量。使用DSL功能可以分成下面四步:

  1. 创建规则文件PersonDSL.dsl,代码如下:
[when] There is a person = $p:Person()
[when] - id greater than {id:\d*} = id > {id}
[then] 学校将张三安排到"{className}"=$p.setClassName("{className}");
[then] print = System.out.println("You are beautiful!");
  1. 创建规则文件isDSL.dslr,代码如下:
package rulesTwo;
import com.saic.entity.Person;
expander personDSL.dsl
rule 'test-dsl'
  when
    There is a person
    - id greater than 10
  then
    学校将张三安排到"3 grade"
    print
end
  1. 配置kmodule.xml,如下我们的规则文件放在src/main/resources/rulesTwo目录下,我们packages配置为rulesTwo。
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">

    <!--
        name:指定kbase的名称,可以任意,但是需要唯一
        packages:指定规则文件的目录,需要根据实际情况填写,否则无法加载到规则文件
        default:指定当前kbase是否为默认
    -->
    <kbase name="dsl" packages="rulesTwo">
        <ksession name="isDSL"/>
    </kbase>
</kmodule>
  1. Fact更新,规则匹配代码类似于2.3章节,这里暂不做赘述。

3.2 其他

Drools还有另外一些高级用法:决策表、规则模版、规则流等,受限于篇幅这里暂不做介绍,感兴趣的读者可以自行翻阅相关文档了解。

四、Workbench

Workbench是KIE组件中的元素,也称为KIE-WEB,是Drools-WB与JBPM-WB的集合体,是一个WEB-IDE,是一个可视化的规则编辑器。Workbench功能十分强大,不仅提供一系规则编辑器,还可以编辑JavaBean。

  1. workbench部署:
    Workbench是一个war包,可以部署在tomcat和wildfly中。笔者本地部署的workbench版本为kie-drools-wb-6.5.0.Final,经多轮测试发现该war包只有在tomcat8以下的版本才能正常部署
    Workbench登录后的首页
图3 登录账号后主页面
  1. 编辑
    第一步:创建一个新的空间及项目
    创建WorkbenchJava工程与项目如图4所示。
    创建工程
图4 创建工程与项目

第二步:创建一个Person数据对象和一个决策树
创建Person对象和决策树

图5 决策树与数据对象在workbeanch中的显示
图6描述的是在workbench编辑页面创建Person数据对象,图7描述的是在workbench编辑页面创建javaTree向导决策树。图5为工程列表界面。

创建Person对象

图6:创建Person对象

创建决策树

图7:创建决策树
  1. Workbeanch部署
    点击Build & Deploy按钮将工程编辑成jar包,部署在本地maven仓库。图8描述的是workbench项目部署功能。
    部署
图8:Workbeanch部署
  1. 动态加载
    workbench部署以后,我们的客户端可以调用Drools的KieScanner的依赖包动态更新规则库,事例代码如下所示:
public class TestWorkbench {
    @Test
    public void testWorkbench() {
        KieServices kieServices = KieServices.Factory.get();
        ReleaseId releaseId = kieServices.newReleaseId("com.WorkbenchJava", "Workbench4Java", "1.0.0");
        KieContainer kieContainer = kieServices.newKieContainer(releaseId);
        KieScanner kieScanner = kieServices.newKieScanner(kieContainer);
        kieScanner.start(1000L);
        while (true) {
            try {
                KieSession kieSession = kieContainer.newKieSession();
                Person person = new Person();
                person.setAge(30);
                person.setName("张三");
                kieSession.insert(person);
                int count = kieSession.fireAllRules();
                System.out.println("Java调用workbench,自动扫描共执行了" + count + " 条规则");
                System.out.println("解决后的结果为: "+ person.getClassName());
                Thread.sleep(1000L);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}

五、android端集成

Drools为Java EE项目,主要运行在服务器。笔者为android开发,尝试在android端集成Drools相关功能。
笔者的开发的软件环境为:

  1. android 9.0
  2. Drools版本 5.2

笔者在android上实现了android的集成,亲测可以实现规则文件的热更新

六、类比其他Java规则引擎

市面上的Java规则引擎很多,笔者将他们分成两类,重量级和轻量级两类。重量级规则引擎功能完备,依赖较多,轻量级依赖少包的体积小。下面罗列了几个规则引擎及其优点,具体使用哪种引擎需要根据自己的业务需求决定。
1)重量级:

  1. Ilog JRules :最知名商业规则管理系统
  2. Drools:开源,最活跃
  3. Visual Rules:最早国内商业规则管理系统

2)轻量级:

  1. Aviator:
    专用脚本语言AviatorScript
    asm模式与解释器模式
  2. Easy rule:
    支持Java和yml两种方式调用
    支持注解
    支持规则组合
  3. QLExpress:
    支持表达式缓存,性能优秀
    安全可控

七、应用场景与展望

规则引擎应用广泛,之前主要应用在在服务端设备。随着移动互联时代的到来,越来越多的移动设备也是用了规则引擎,使用方式笔者认为大体分为三类:

  1. 自研规则引擎,比如语音DM;
  2. 完全使用开源规则引擎,例android集成Drools,Drools功能完善,但是比较重,学习成本高,灵活度有所欠缺;
  3. 使用规则引擎部分功能,高合汽车场景引擎采用QLExpress做规则判断,其余功能自研,这类方案自由度大一些。

随着智能座舱概念成为主流,越来越多的车载应用需要支持动态配置,规则引擎的引用也是越来越多,使用者需要根据业务需求选择合适的规则引擎。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值