规则引擎,顾名思义通过执行指定的规则集合,得到一系列结果。简单地说,存在业务规则集合(r1,r2…rn),数据集合(d1,d2…dn),规则之间是独立的,规则引擎进行模式匹配,并执行匹配的规则。根据不同的匹配过程,可以把规则引擎分为两种,无状态会话(stateless session)和有状态会话(stateful session)。
2.无状态会话(Stateless session)
无状态会话的规则引擎,不会进行推理。无状态的规则引擎,等价于顺序处理数据的函数方法集合,而且仅仅执行一遍函数集合。按照顺序执行函数集合后,得到期望的结果集合。
无状态会话不保存状态,为了执行规则引擎,初始时必须加入所有对象,并且在执行的过程中不会加入新的对象。由于对象集合没有变化,所以规则集合仅仅需要执行一次。
2.1适用场景:
(1) 校验过程,由校验规则构成规则集合。
(2) 计算过程,由计算公式等构成规则集合。
(3) 路由和过滤,由路由规则和过滤规则构成规则集合。
2.2 实例说明(实例都来自Drools的官方用户手册中)
以Drools的开源实现,说明无状态会话的规则引擎如何使用。举一个驾照申请的例子,假定只有年龄大于18岁的申请者是有效的。那么,Drools的规则可以这么写:
package com.company.license rule "Is of valid age" when $a : Applicant( age < 18 ) then $a.setValid( false ); end
简单解释下这个规则,when表示规则,then表示结果。这个规则的意思是:在Applicant对象中,age属性值小于18时,valid属性置为false。
Drools的规则加载过程,通过KnowledgeBuilder和KnowledgeBase完成。KnowledgeBuilder负责对规则进行解析,解析的结果保存在KnowledgeBase中。假定上述的规则保存在licenseApplication.drl中,那么Drools的加载过程如下。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("licenseApplication.drl",getClass()),ResourceType.DRL ); if ( kbuilder.hasErrors() ) { System.err.println( kbuilder.getErrors().toString() ); } KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(); kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
规则加载完成之后,如果规则解析的过程中不存在错误。那么,可以新建一个会话,然后根据数据匹配和执行规则。
StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession(); Applicant applicant = new Applicant( "Mr John Smith", 16 ); assertTrue( applicant.isValid() ); ksession.execute( applicant ); assertFalse( applicant.isValid() );
上面的代码新建了一个无会话状态的规则引擎,当对Applicant对象执行了规则之后,Applicant对象的Valid属性被置为False。
3.有状态会话(stateful session)
有状态会话,能够在多次规则执行的时候保存状态。不同于无状态的会话,有状态的会话存在时间更长,当数据发生变化以后,会重复执行规则的匹配。也就是说,它们之间最大的区别在于,有状态会话存在推理过程。如果一条规则改变了数据,那么所有规则可能需要重新匹配一次。
3.1适用场景:
(1) 监控,例如股票行情的监控和分析。
(2) 诊断,医学诊断。
(3) 物流,包裹的运输和投递。
3.2实例说明
举一个房间火警的例子,说明有状态会话的特征。系统中有4种类型,分别表示房间、自动灭火装置、火、警报。
public class Room { private String name // getter and setter methods here } public classs Sprinkler { private Room room; private boolean on; // getter and setter methods here } public class Fire { private Room room; // getter and setter methods here } public class Alarm { }
从上面的代码,我们可以看到每种类型的实体对象不再唯一。可能有多个房间,每个房间有一个自动灭火装置,所有房间共用一个警报。这种情况下,对象之间的关系。
存在如下规则:
规则一、当某个房间起火时,灭火装置自动打开。从规则引擎方面理解,有Filre类型的对象加入到会话中时,对应房间的Sprinkler对象的开关打开。规则描述如下:
rule "When there is a fire turn on the sprinkler" when Fire($room : room) $sprinkler : Sprinkler( room == $room, on == false ) Then modify( $sprinkler ) { setOn( true ) }; System.out.println( "Turn on the sprinkler for room " + $room.getName() ); end
规则二、当火被扑灭的时候,那么检查并关闭每个房间的灭火装置。对应的,规则引擎中不存在Fire类型对象,那么检查并关闭每个Room类型对象对应的Sprinkler对象中的开关。规则描述如下:
rule "When the fire is gone turn off the sprinkler" when $room : Room( ) $sprinkler : Sprinkler( room == $room, on == true ) not Fire( room == $room ) then modify( $sprinkler ) { setOn( false ) }; System.out.println( "Turn off the sprinkler for room " + $room.getName() ); End
规则三、只要有火灾存在,那么系统中唯一的警报发出告警。相对应的,规则引擎中存在Fire类型对象,那么该规则向session中添加Alarm类型对象。对应规则描述如下:
rule "Raise the alarm when we have one or more fires" when exists Fire() then insert( new Alarm() ); System.out.println( "Raise the alarm" ); End
规则四、当所有火灾被扑灭之后,警报关闭。相对应的,规则引擎中不存在Fire对象,移除Alarm类型的对象。
rule "Cancel the alarm when all the fires have gone" when not Fire() $alarm : Alarm() then retract( $alarm ); System.out.println( "Cancel the alarm" ); End
规则五、没有火灾发生时,系统状态是OK的。对应规则引擎中,没有Alarm类型的对象,并且灭火装置处在关闭状态。
rule "Status output when things are ok" when not Alarm() not Sprinkler( on == true ) then System.out.println( "Everything is ok" ); End
规则的加载同于无状态会话的过程。规则引擎的创建:
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add( ResourceFactory.newClassPathResource( "fireAlarm.drl", getClass() ), ResourceType.DRL ); if ( kbuilder.hasErrors() ) { System.err.println( builder.getErrors().toString() ); } kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() ); StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
规则引擎的执行过程一、:
String[] names = new String[]{"kitchen", "bedroom", "office", "livingroom"}; Map<String,Room> name2room = new HashMap<String,Room>(); for( String name: names ){ Room room = new Room( name ); name2room.put( name, room ); ksession.insert( room ); Sprinkler sprinkler = new Sprinkler( room ); ksession.insert( sprinkler ); } ksession.fireAllRules()
这段代码的执行过程中,仅仅匹配了规则五。执行结果是,Everything is ok。
规则引擎的执行过程二:
Fire kitchenFire = new Fire( name2room.get( "kitchen" ) ); Fire officeFire = new Fire( name2room.get( "office" ) ); FactHandle kitchenFireHandle = ksession.insert( kitchenFire ); FactHandle officeFireHandle = ksession.insert( officeFire ); ksession.fireAllRules();
这段代码向会话中,放入两个Fire类型的对象,规则一和三匹配成功。
> Raise the alarm > Turn on the sprinkler for room kitchen > Turn on the sprinkler for room office
4.小结
有状态会话和无状态会话之间,主要区别在于是否会动态添加或者移除对象和规则的推理。会话状态的选择完全取决于业务的需求。如果在规则执行之前,就能确定所有的对象,那么多数情况下需要使用无状态的规则引擎。反之,如果规则引擎需要多次执行,每次都依赖之前的状态,那么选择有状态的规则引擎是很恰当的。
参考文档:
1.Drools Expert User Guide
2.http://www.ibm.com/developerworks/cn/java/j-java-rules/index.html?ca=dwcn-newsletter-java