文章目录
背景
笔者最近在研究、使用Java规则引擎Drools,主要工作分两部分:1. Java EE环境下的Drools、Workbench的使用;2. Drools android环境的移植工作,本篇博客主要也是按照这两部分内容分别介绍的。
一、Drools基本概念
1.1 规则引擎
Drools是Java规则引擎,关于规则引擎的定义,笔者从百度百科摘录了如下定义
规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。
从规则的定义可知,一个完整的规则引擎应该包含:
- 规则定义
- 规则解析
- 决策执行
笔者调研过各种java规则引擎,大多数轻量级规则引擎包含上述的两种及全部。各类引擎也都是在一种或者多种领域个性化,扩充能力。
1.2 Drools简介
Drools的定义,笔者也从百度百科摘录了定义,结果如下
Drools(JBoss Rules )具有一个易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。
Drools有如下特点:
- 完全开源现在仍在持续更新;
- 基于Charles Forgy的RETE算法的规则引擎的实现;
- 使用XML的、 节点表达If–Then句式,而里面可以嵌入上述语言的代码作为判断语句和执行语句。
二、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
从一个标准文件,可以看出:
- 一个规则文件包含:包名、引用、自定义函数、查询、对象申明、规则体等;
- 一个规则体包含规则名、LHS、RHS;
- 绑定变量名:[绑定变量名:Object(Field 约束)];
- 规则文件与Java可以实现双向调用;
- 有一点需要注意的是,运算符(+、-、*、/、%)与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>
- kmodule.xml文件存放在src/main/resources/META-INFO文件夹下。一个kmodule.xml配置中可包含多个KieBase,每一个KieBase都有name属性,可以取任意字符串,但不能重名;
- KieBase有一个packages属性,其内容指向src/main/resources目录下文件夹的名称,规则引擎会根据packages定义的内容查找规则文件(文件夹下有子文件夹不会被加载);
- 每一个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功能可以分成下面四步:
- 创建规则文件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!");
- 创建规则文件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
- 配置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>
- Fact更新,规则匹配代码类似于2.3章节,这里暂不做赘述。
3.2 其他
Drools还有另外一些高级用法:决策表、规则模版、规则流等,受限于篇幅这里暂不做介绍,感兴趣的读者可以自行翻阅相关文档了解。
四、Workbench
Workbench是KIE组件中的元素,也称为KIE-WEB,是Drools-WB与JBPM-WB的集合体,是一个WEB-IDE,是一个可视化的规则编辑器。Workbench功能十分强大,不仅提供一系规则编辑器,还可以编辑JavaBean。
- workbench部署:
Workbench是一个war包,可以部署在tomcat和wildfly中。笔者本地部署的workbench版本为kie-drools-wb-6.5.0.Final,经多轮测试发现该war包只有在tomcat8以下的版本才能正常部署。
- 编辑
第一步:创建一个新的空间及项目
创建WorkbenchJava工程与项目如图4所示。
第二步:创建一个Person数据对象和一个决策树
- Workbeanch部署
点击Build & Deploy按钮将工程编辑成jar包,部署在本地maven仓库。图8描述的是workbench项目部署功能。
- 动态加载
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相关功能。
笔者的开发的软件环境为:
- android 9.0
- Drools版本 5.2
笔者在android上实现了android的集成,亲测可以实现规则文件的热更新。
六、类比其他Java规则引擎
市面上的Java规则引擎很多,笔者将他们分成两类,重量级和轻量级两类。重量级规则引擎功能完备,依赖较多,轻量级依赖少包的体积小。下面罗列了几个规则引擎及其优点,具体使用哪种引擎需要根据自己的业务需求决定。
1)重量级:
- Ilog JRules :最知名商业规则管理系统
- Drools:开源,最活跃
- Visual Rules:最早国内商业规则管理系统
2)轻量级:
- Aviator:
专用脚本语言AviatorScript
asm模式与解释器模式 - Easy rule:
支持Java和yml两种方式调用
支持注解
支持规则组合 - QLExpress:
支持表达式缓存,性能优秀
安全可控
七、应用场景与展望
规则引擎应用广泛,之前主要应用在在服务端设备。随着移动互联时代的到来,越来越多的移动设备也是用了规则引擎,使用方式笔者认为大体分为三类:
- 自研规则引擎,比如语音DM;
- 完全使用开源规则引擎,例android集成Drools,Drools功能完善,但是比较重,学习成本高,灵活度有所欠缺;
- 使用规则引擎部分功能,高合汽车场景引擎采用QLExpress做规则判断,其余功能自研,这类方案自由度大一些。
随着智能座舱概念成为主流,越来越多的车载应用需要支持动态配置,规则引擎的引用也是越来越多,使用者需要根据业务需求选择合适的规则引擎。