1:什么是规则引擎?
规则引擎是由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。
大多数规则引擎都支持规则的次序和规则冲突检验,支持简单脚本语言的规则实现,支持通用开发语言的嵌入开发。目前业内有多个规则引擎可供使用,其中包括商业和开放源码选择。开源的代表是Drools,商业的代表是Visual Rules ,I Log。
2:什么是Drools?以及Drools的优点。
Drools是一款开源的用java语言编写的规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中。
3:Drools使用概览。
Drools在使用的过程中主要分为两个部分:一是Drools规则,二是Drools规则的解释执行。规则的编译与运行要通过Drools 提供的各种API 来实现,这些API 总体来讲可以分为三类:规则编译、规则收集和规则的执行。
4:Drools 5.x使用。
Drools5.x完成规则的收集执行主要依赖以下API:KnowledgeBuilder、KnowledgeBase、StatefulKnowledgeSession、StatelessKnowledgeSession。
4.1:KnowledgeBuilder
KnowledgeBuilder的作用就是用来在业务代码当中收集已经编写好的规则, 然后对这些规则文件进行编译, 最终产生一批编译好的规则包(KnowledgePackage)给其它的应用程序使用。
KnowledgeBuilderkbuilder=KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("test.drl",Test.class),ResourceType.DRL);
Collection<KnowledgePackage>kpackage=kbuilder.getKnowledgePackages();//产生规则包的集合。
4.2:KnowledgeBase
KnowledgeBase是Drools 提供的用来收集应用当中知识(knowledge)定义的知识库对象,在一个KnowledgeBase 当中可以包含普通的规则(rule)、规则流(rule flow)、函数定义(function)、用户自定义对象(type model)等。创建一个KnowledgeBase 要通过KnowledgeBaseFactory 对象提供的newKnowledgeBase()方法来实现,这其中创建的时候还可以为其指定一个KnowledgeBaseConfiguration对象,KnowledgeBaseConfiguration对象是一个用来存放规则引擎运行时相关环境参数定义的配置对象。
KnowledgeBaseConfigurationkbConf
=KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
kbConf.setProperty("org.drools.sequential", "true");
KnowledgeBasekbase =KnowledgeBaseFactory.newKnowledgeBase(kbConf);
kbase.addKnowledgePackages(kpackage);//将KnowledgePackage集合添加到KnowledgeBase当中
4.3:StatefulKnowledgeSession
StatefulKnowledgeSession的主要作用是将编译好的规则文件运行起来。与之相似的还有StatelessKnowledgeSession,后者将在下一小节中介绍。
StatefulKnowledgeSession可以接受外部插入(insert)的业务数据——也叫fact,一个fact 对象通常是一个普通的Java 的POJO,一般它们会有若干个属性,每一个属性都会对应getter 和setter 方法,用来对外提供数据的设置与访问。一般来说,在Drools 规则引擎当中,fact 所承担的作用就是将规则当中要用到的业务数据从应用当中传入进来,对于规则当中产生的数据及状态的变化通常不用fact 传出。如果在规则当中需要有数据传出,那么可以过在StatefulKnowledgeSession当中设置global 对象来实现,一个global 对象也是一个普通的Java 对象,在向StatefulKnowledgeSession 当中设置global 对象时不用insert 方法而用setGlobal 方法实现。
StatefulKnowledgeSessionstatefulKSession=kbase.newStatefulKnowledgeSession();
statefulKSession.setGlobal("globalTest",new Object());//设置一个global对象
statefulKSession.insert(newObject());//插入一个fact对象
statefulKSession.fireAllRules();
statefulKSession.dispose();
4.4:StatelessKnowledgeSession
StatelessKnowledgeSession对StatefulKnowledgeSession做了包装,使得在使用StatelessKnowledgeSession对象时不需要再调用dispose()方法释放内存资源了。
4.5:Fact对象
将一个普通的JavaBean 插入到规则的WorkingMemory当中后的对象。规则可以对Fact 对象进行任意的读写操作,当一个JavaBean 插入到WorkingMemory 当中变成Fact 之后,Fact 对象不是对原来的JavaBean 对象进行Clon,而是原来JavaBean 对象的引用。。一个Fact 对象通常是一个具有getter 和setter 方法的POJO 对象,通过这些getter 和setter 方法可以方便的实现对Fact 对象的读写操作,Fact对象一般就是要处理的业务对象。
4.6:Global对象
可以往WorkingMemory中插入Global对象,Global对象可以往外输出值。主要是通过Fact对象的Setter和Getter方法能对属性进行的修改有限,可以通过global对象进行更加复杂的修改。
5:Drools规则简介(适用至Drools6.3)。
在 Drools 当中,一个标准的规则文件就是一个以“.drl”结尾的文本文件,由于它是一个标准的文本文件,所以可以通过一些记事本工具对其进行打开、查看和编辑。规则是放在规则文件当中的,一个规则文件可以存放多个规则,除此之外在规则文件当中还可以存放用户自定义的函数、数据对象及自定义查询等相关在规则当中可能会用到的一些对象。
5.1:规则文件的基本格式。
packagepackage-name
imports
globals
functions
queries
rules
rules代表的是规则文件中的各种规则。
值得注意的是此处的package与java的package并不相同,Java语言中的package 管理既有物理上Java 文件位置的管理也有逻辑上的文件位置的管理,而drools规则文件中的package只进行逻辑上的管理。对于同一package 下的用户自定义函数、自定义的查询等,不管这些函数与查询是否在同一个规则文件里面,在规则里面是可以直接使用的。
5.1:规则文件中规则的基本格式。
rule"name"
attributes
when
LHS
then
RHS
end
一个规则主要可以分成3个部分,属性部分、条件部分和结果部分。
5.2:规则文件中条件的详解。
条件部分被称为Left Hand Side,简称为LHS,在LHS 当中,可以包含0~n 个条件,如果LHS 部分没空的话,那么引擎会自动添加一个eval(true)的条件,由于该条件总是返回true,所以LHS 为空的规则总是返回true。条件又称之为pattern(匹配模式),多个pattern之间用可以使用and 或or 来进行连接,同时还可以使用小括号来确定pattern 的优先级。条件又称之为pattern(匹配模式),多个pattern之间用可以使用and 或or 来进行连接,同时还可以使用小括号来确定pattern 的优先级。
示例:$customer:Customer(age>20,gender==’male’)
该pattern 有三个约束,分别是:对象类型必须是Cutomer;同时Cutomer 的age 要大于20 且gender 要是male;对于对象内部的多个约束的连接,可以采用“&&”(and)、“||”(or)和“,”(and)来实现。如下:Customer(age>20 || (gender==’male’&&city==’sh’))。在Drools中提供了12种比交符:>、>=、<、<=、= =、!=、contains、not contains、memberof、not memberof、matches、not matches。
注意:比较操作符contains 是用来检查一个Fact 对象的某个字段(该字段要是一Collection或是一个Array 类型的对象)是否包含一个指定的对象。not contains与之相反。
matches是用来对某个Fact 的字段与标准的Java 正则表达式进行相似匹配,被比较的字符串可以是一个标准的Java 正则表达式,但有一点需要注意,那就是正则表达式字符串当中不用考虑“\”的转义问题。
$customer:Customer(namematches "李.*");
与之正好相反的是not matches。
5.3:结果部分。
结果部分又被称之为Right Hand Side,简称为RHS,在一个规则当中then 后面部分就是RHS,只有在LHS 的所有条件都满足时RHS 部分才会执行。
在RHS部分可以修改Fact对象和Global对象,在RHS部分还可以直接编写java代码。Drools 5.x 提供了如insert/insertLogical、update 和retract 就可以实现对当前Working Memory
中的Fact 对象进行新增、删除或者是修改。
一旦调用insert 宏函数,那么Drools 会重新与所有的规则再重新匹配一次,对于没有设这个特性不仅存在于insert 宏函数上,后面介绍的update、retract 宏函数同样具有该特性,所以在某些情况下因考虑不周调用insert、update 或retract 容易发生死循环。
then
Customercus=new Customer();
cus.setName("张三");
insert(cus);
update函数主要对working memory中的对象进行更新
5.4:属性部分
Drools5当中,规则的属性共有13 个,它们分别是:activation-group、agenda-group、auto-focus、date-effective、date-expires、dialect、duration、enabled、lock-on-active、no-loop、ruleflow-group、salience、when,这些属性分别适用于不同的场景。
使用salience属性控制规则执行的优先级,使用int值确定其大小。Salience属性越大,优先级越高,规则的执行越靠前。
使用no-loop属性可以避免因为规则中Fact对象的增删改等造成的规则的循环执行。no-loop 属性的作用是用来控制已经执行过的规则在条件再次满足时是否再次执行。
enabled属性控制规则是否生效,设置为false的属性将直接不执行。
Dialect属性控制Drools使用的语言,默认的是java,还可以设置成mvel。
drools.getRule().getDialect()。
activation-group属性是将若干规则分到一个组里,这个组里的规则只有一个会被执行,执行的先后顺序可以通过salience属性进行控制。
5.5:Drools宏对象。
通过使用drools 宏对象可以实现在规则文件里直接访问Working Memory。
halt()drools.halt() 在当前规则执行完成后,不再执行其它未执行的规则。
5.6:Drools注释。
Drools中的注释和Java中的注释一样,可以分为单行注释和多行注释,单行注释使用”//”实现,多行注释与java相同。
5.7:未讲解部分。
一是函数二是Drools查询部分(主要是对Working Memory中的Fact对象进行查找)。
6:Drools6.x使用简介(适用至Drools6.3)。
6.1:安装Eclipse的drools插件。
从eclipse help->installnew software, 安装drools插件。(如果不直接使用eclipse创建drl文件或者创建drools项目的话不需要使用drools插件)。安装插件的方式除了使用在线安装的话也可以使用安装本地插件的方式,drools官网下载安装包解压缩后添加此插件。安装完成后重启即可。
6.2:Drools 6.x使用基本示例代码。
KieServicesks = KieServices.Factory.get();
KieContainerkContainer = ks.getKieClasspathContainer();
KieSessionkSession = kContainer.newKieSession("ksession-rules");
kSession.insert(“待插入Fact对象”);
kSession.setGlobal(“规则文件中的global名称”,插入的对象);
kSession.fireAllRules();//执行规则
注解:kie框架先初始化一个kieservice对象,然后从classpath中读取kmodule,创建KieContainder, 从而获取KieSession来对Fact(message对象)执行规则(fireAllRules)。
6.3:Drools 6.x相关配置文件简介。
规则文件存放目录如下图所示:
kmodule.xml就是kieservice从classpath需要读取的kmodule配置文件,这个配置文件决定了kession的类型,名称,rules文件所在文件夹(kbase packages)。配置的示例代码如下所示:
<?xmlversion="1.0" encoding="UTF-8"?>
<kmodulexmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="myKbase"packages="exclusive">
<ksessionname="session-mySession" />
</kbase>
</kmodule>
Drools6.x 最大不同就是把rules打包成jar,使用端通过kie-ci来动态从maven repo中获取指定rules jar版本,pom.properties。因为新版本的drools依靠maven来区分不同的kmodule从而实习方便的动态rules upgrade,打开pom.properties,将其设置如下
groupId=${project.groupId}
artifactId=${project.artifactId}
version=${project.version}
这样设置的效果是drools文件规则的“版本”随着项目版本的变化而不断变化。
6.4:Maven项目中直接引入Drools相关依赖。
Drools项目本身就是一个Maven项目,因此可以直接在Maven项目中引入相关pom依赖即可在项目中使用Drools的相关API。需要注意的一点是drools中本身依赖了一些其他的jar包,如果先关jar包没有引用对的话会出现错误。本人在实践中所犯的一项错误就是Drools 6.x依赖的protobuf的pom包要求2.5.0以上版本,而本人的pom文件中所使用的版本是2.4.x,最终项目出错,不能正确运行。
<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>knowledge-api</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-jsr94</artifactid>
<version>${drools.version}</version>
</dependency>
<plugin>
<groupId>org.kie</groupId>
<artifactId>kie-maven-plugin</artifactId>
<version>${drools.version}</version>
<extensions>true</extensions>
</plugin>
6.5:Drools 6.x动态生成规则。
实际项目的使用中有两种方式实用规则,一是前文简介的使用Drools文件,二就是动态的加载规则,规则以其他形式存储在数据库或者文件系统之中。个人感觉两者的区别是drools规则文件表示的规则是简单的规则,业务人员可以直接上手编写的。而动态加载规则往往是规则更加复杂,规则条数更多,规则的不确定性更高。在具体的使用当中可以结合具体的业务场景选择使用方式。基本代码框架如下所示:
KieServiceskieServices = KieServices.Factory.get();
ReleaseIdreleaseId1 = kieServices.newReleaseId("org.autonavi","test-kie-exclusive",String.valueOf(date.getTime()));
KieFileSystemkfs = kieServices.newKieFileSystem().generateAndWritePomXML(releaseId1);
StringBuffersb = new StringBuffer("package com.autonavi.wym.exclusive \n\n");
sb.append("importcom.autonavi.wym.entity.feedback.FeedBackInfo;\n");
sb.append("globaljava.util.List list; \n\n");
//拼接规则部分(代码省略)
//拼接结果部分(代码省略)
sb.append("end");
kfs.write("src/main/resources/exclusive/"+ date.getTime() + UUID.randomUUID().toString()
+"exclusiveRule.drl", sb.toString().getBytes("UTF-8"));
KieBuilderkb1 = kieServices.newKieBuilder(kfs);
kb1.buildAll();
KieContainerkContainer1 = kieServices.newKieContainer(releaseId1);
KieSessionkSession1 = kContainer1.newKieSession();//生成KieSession
拿到新生成的KieSession后就可以愉快的插入fact对象然后重新执行规则。
6.6:Drools 6.x动态生成规则中的注意事项。
在实际使用的过程中需要注意以下几点:
①:采用上述方法生成规则文件的时候出现中文字符的时候需要将中文字符转化为utf-8编码格式的字符。
②:同样也要将web容器启动时的系统语言设置成utf-8。
③:规则的名称不允许重复
④:生成的规则文件的名称不允许重复,当出现重复的情况后生成的规则将覆盖原先生成的同样文件名的文件。
⑤:以上方法并不会生成真正存在于文件系统上的文件,所以所生成的规则中包名和最后生成的文件名都可以是实际中不存在的文件路径。建议可以用与业务相关的包名和规则名表示所声称的规则,这样实际中查看起来会一目了然。
6.7:使用场景简介。
Drools的使用在不同的项目中目的侧重点几乎都一样,就是为了减少业务规则变化所带来的代码的更改。本次使用的场景是在内部的业务处理系统中,不同的用户具有对不同问题的处理权限(权限具体的划分就是依据问题的属性进行划分),这样不同的用户在处理问题领取问题的时候都需要去依据自身的权限去查找能被自己处理的问题。当问题具有很多属性的时候往往会形成很复杂的sql语句,不仅在拼接查询条件的过程中容易出错,而且因为查询语句所涉及的属性很多往往会造成查询效率的下降。为解决这一问题,将规则引擎引入到项目中,当问题进来的时候提前使用规则引擎依据用户组的权限对问题的属性进行判断,将问题提前分配到用户组中。这样以后用户在处理问题的过程中只需要依据自身的用户类型进行问题的查找就完成权限的控制了,使得业务上可以达到专人处理专项问题,同时程序的效率得到大幅提升。