JBossRules 规则引擎

1.概述

网站:http://labs.jboss.com/jbossrules

完整文档:http://labs.jboss.com/portal/jbossrules/docs/index.html

详细的文档中文翻译与学习笔记:http://www.blogjava.net/guangnian0412/ 

Drools 变身为JBoss Rules 3.0后已经拥有了好得多规则语法,平民级的DSL语言和基于Eclipse的编辑器,前景明亮。

其中平民级的DSL语法促使规则引擎这个有点神秘的高端很有机会在2007年大面积应用于各个普通开发团队,如Spring,Hibernate一样平常。

Drools下载中有一个Samples下载包有完整的demo代码,SpringSide中主要应用Drools作订单计价。

1.1 一次Drools的规则运算通常如下:

1. 从Drl规则文件编译得到RuleBase--编译后的规则集;

2. 从RuleBase生成本次规则运算的场地--WorkingMemory;

3. 将规则运算用到的事实放入WorkingMemory;

4. FireAll Rules,对事实进行规则运算。

1.2 Drools的规则语法

不再同以前的XML版语法,现在的[When] 结构中,When节点包含了autoboxing等简化语法,没有Java原版equals语法的冗长。

一个完整的drl 文件如下:


/*
* multi comments
*/

package
org.springside.rules.orderPricing


import org.springside.facts.factsObject


expander another_drl_or_dsl_files

// some comments

rule "rule_name"
agenda-group "group_name"
salience -1
no-loop false
auto-focus false
activation-group "xor_group_name"
duration 0

#some comments
when
order : Order( totalPrice >= 100)
then
order.setDiscountPrice(new Double(order.getTotalPrice().doubleValue() * 0.9));
end

具体的drl 属性定义,请查阅drools 官方网站文档
这里需要注意的是:then 子句中为纯粹的java 表达式,所以每句必须要以";" 结束。而在其他的地方可以不用。

同时,你也可以选用JavaScript解释引擎,在then子句中使用JavaScript语法 

1.3 Drools的平民级DSL语法映射

Drools的DSL采用了直接映射而不是Yacc,Antrl之类的语义分析是我最喜欢的地方,唯有如此,普通团队才可以用上对客户充满诱惑,对团队本身也能清晰定义的DSL, 而且,这DSL还能获得Eclipse插件的支持

只要声明一个DSL映射文件

[when]order price larger than {topPrice}=order : Order( totalPrice >= {topPrice} )

#如果多行,可以在第一行用"/" 进行换行
[then]do {discountRate}discount= order.setDiscountPrice/(new Double(order.getTotalPrice().doubleValue() * {discountRate}));

就可以这样使用

expander orderPricing.dsl
rule "discount order"
  when
  order price larger than 100
 then
  do 0.9 discount
 end

1.4 决策树(Excel 文件)的使用

具体的使用,可以查看官方文档。这里Drools 有点取巧,它是首先读取Excel 文件,把每一行决策作为一个Rule。然后把Excel 文件转换成drl 格式的文本,再调用drl 规则的API 构建规则的。并且,在Excel 文件中定义的Rule 是有执行顺序的。它会按照行数由上自下执行。

[TODO]:

1.5 Drools' Performance

3.0 相对于2.5 以来,在效率方面有了大大的提升。现在的规则执行速度上面已经基本上达到了JRule6 的水平。

对于执行效率,有几点需要注意的地方:

1、Condition 的排放顺序。如果你的规则中Condition 不只一个的话,那么把哪个Condition 放在前面是很有讲究的,这直接关系到规则引擎的执行效率。

例如下面,有上万个如下类型的facts 将同时assert 进入规则引擎中参与计算。而这些facts 中绝大部分facts 的type 为1,name 却各不相同:

/*
* fact class
*/
public class FactObject {
private int type;
private String name;
// 省去getter/setter Methods
}

规则文件如下定义:

when
fact : FactObject(type == 1, name == "test")

和这样定义:

when
    fact : FactObject(name == "test", type == 1)

后面这种定义相对于前面,单单是换了一个顺序,在执行效率方面相差几十倍!其原理和数据库索引的原因相似。

2、还是如上面的这个例子,对于字符串,如果在比较之前使用intern() 的话,那么效率也会部分提升。
参见JBoss Wiki:http://wiki.jboss.org/wiki/Wiki.jsp?page=StringIntern

1.6 注意事项

1、这里最需要注意的是Drools 最严重的一个Bug:关键字冲突。如果使用了任何与关键字相同的名称,包含包名、组名、dsl 定义中的单词,以及函数名称,那么这个drl 文件将会在构建时候报错。包含这样的使用:package org.springside.rule

现把Drools 的所有关键字列出:
when then rule end contains matches and or modify retract assert salience function
query exists eval agenda-group no-loop duration -> not auto-focus

这个Bug 将在Drools 3.1M1 中解决。

2、在dsl 定义中,不能使用标点。如:Order's price larger than 100。这样的定义也无法被编译。

2.SpringSide的扩展

SpringSide对单次执行的规则进行了简单封装, 让用户可以用一句API调用就使用到规则引擎.

而对于复杂的规则引擎API使用, SpringSide将只返回WorkingMemory或RuleBase, 让用户自行调用JBoss Rules的原版API,思路和Spring对Hibernate的封装,关键时候返回sessionFactory让用户自行调用一样.

2.1 规则分组及管理

 不得不说,drools 自3.0 后对于规则的管理大大加强了,这也是springside 将drools 升级到3.0 后,放弃数据库管理模块的一个原因。

在2.5 中,我们是通过每一个规则文件,都构建一个RuleBase 来对规则进行分组以及管理的。但是自从进入3.0 后,我们只存在一个RuleBase 。而每个drl 文件上定义的package 将作为Rule 管理的基本单元,每个Rule 中的属性agenda-group 将作为规则分组的基本单元。

2.2 封装DroolsTemplate

将Drools的底层代码全部封装起来,只留一个RuleSTemplate接口,有如下函数:

  public List executeRules(List facts, String groupName, String ruleNameFilter)

第一个参数--参与规则运算的事实表;
第二个参数--所调用的规则集名称,对应的为drl 文件中rule 的agenda-filter 属性。如果用null 则表示不限定规则集,fire 所有的规则。另外,在agenda-filter 中有一个默认的规则组“MAIN”,在此参数中,使用"MAIN" 和 null 等同。
第三个参数--规则名称过滤,现使用的为StartsWith 规则,也就是规则名称starts with the ruleNameFilter, 此规则才会被fire 。如果有需要,可以通过修改ss 的DroolsTemplate 文件获得不同的规则名称过滤。如果为null 则表示不对规则名称过滤。

用户使用时只需参照ApplicationContext-rule.xml 配置一把,就能在代码里使用简单的RuleTemplate接口进行规则运算。

2.3 Application Context文件讲解

DroolsTemplate 支持从数据库或drl文件中获得规则,只需为DroolsTemplate注入不同的RuleLoader即可。

 <bean id="dslRuleBaseLoader" class="org.springside.modules.rule.support.DSLRuleBaseLoader">
        <property name="ruleFiles" value="classpath*:rules/dsl/**.drl"/>
        <property name="dslFile" value="/rules/dsl/orderPricing.dsl"/>
    </bean>
<bean id="ruleTemplate" class="org.springside.modules.rule.support.RuleTemplateImpl">
        <property name="ruleBaseLoader" ref="dslRuleBaseLoader"/>
</bean>

留意DSLRuleBaseLoader的配置,使用Spring Style的文件名称匹配。

Drools 可以说是开源的规则引擎中最好的一个了。springside 中,只是用了Drools 中通用的部分。相信大家如果深入的去研究的话,肯定每次都会有新的发现:)