rule的salience(突出性)属性,定义rule的执行顺序
属性值越高,执行的优先级越高。
RuleB虽然定义在RuleA的后边,但salience值比较高,所以总优先执行。
rule "RuleA"
salience 95
when
$fact : MyFact( field1 == true )
then
System.out.println("Rule2 : " + $fact);
update($fact);
end
rule "RuleB"
salience 100
when
$fact : MyFact( field1 == false )
then
System.out.println("Rule1 : " + $fact);
$fact.setField1(true);
update($fact);
end
rule group的agenda属性,定义一组rule和其他组的执行顺序
通过设置agenda-group的属性,将一组rule绑定到一个组中。
在同一时刻,只有一个组被focus,其中的rule将会被执行。
可以为rule设置auto-focus属性,那么rule所在的组会自动被focus。
默认情况下,所有没有设置group的rule都会放到"MAIN"组中。如果其他组也没有设置focus,那么"MAIN"中的将会在所有的组之前执行。
例子:
rule "Increase balance for credits"
agenda-group "calculation"
when
ap : AccountPeriod()
acc : Account( $accountNo : accountNo )
CashFlow( type == CREDIT,
accountNo == $accountNo,
date >= ap.start && <= ap.end,
$amount : amount )
then
acc.balance += $amount;
end
rule "Print balance for AccountPeriod"
agenda-group "report"
when
ap : AccountPeriod()
acc : Account()
then
System.out.println( acc.accountNo +
" : " + acc.balance );
end
report必须在calculation之前执行,且这两个分组在所有的rule之前执行,则需要在java中如下设置。
Agenda agenda = ksession.getAgenda();
agenda.getAgendaGroup( "report" ).setFocus();
agenda.getAgendaGroup( "calculation" ).setFocus();
ksession.fireAllRules();
也可以取消一个组内所有rule的执行。
ksession.getAgenda().getAgendaGroup( "Group A" ).clear();
activation-group控制一组rule的排他性
activation-group下只有一个符合条件的rule执行。
rule "Print balance for AccountPeriod1"
activation-group "report"
when
ap : AccountPeriod1()
acc : Account()
then
System.out.println( acc.accountNo +
" : " + acc.balance );
end
rule "Print balance for AccountPeriod2"
activation-group "report"
when
ap : AccountPeriod2()
acc : Account()
then
System.out.println( acc.accountNo +
" : " + acc.balance );
end
只会执行一个。
rule执行模式以及drools引擎的线程安全
被动模式
默认模式。
明确调用fireAllRules()触发rule。
Drools引擎中的被动模式最适合于需要直接控制规则评估和执行的应用程序,或者适合于在Drools引擎中使用伪时钟实现的复杂事件处理(CEP)应用程序。
KieSessionConfiguration config = KieServices.Factory.get().newKieSessionConfiguration();
config.setOption( ClockTypeOption.get("pseudo") );
KieSession session = kbase.newKieSession( conf, null );
SessionPseudoClock clock = session.getSessionClock();
session.insert( tick1 );
session.fireAllRules();
clock.advanceTime(1, TimeUnit.SECONDS);
session.insert( tick2 );
session.fireAllRules();
clock.advanceTime(1, TimeUnit.SECONDS);
session.insert( tick3 );
session.fireAllRules();
session.dispose();
主动模式
用户调用fireUntilHalt()将会开启主动模式。程序会不断处理,直到调用了halt()。
Drools引擎中的Active模式最适合将规则评估和执行的控制权委托给Drools引擎的应用程序,或者适合使用Drools引擎中的实时时钟实现的复杂事件处理(CEP)应用程序。
KieSessionConfiguration config = KieServices.Factory.get().newKieSessionConfiguration();
config.setOption( ClockTypeOption.get("realtime") );
KieSession session = kbase.newKieSession( conf, null );
new Thread( new Runnable() {
@Override
public void run() {
session.fireUntilHalt();
}
} ).start();
session.insert( tick1 );
... Thread.sleep( 1000L ); ...
session.insert( tick2 );
... Thread.sleep( 1000L ); ...
session.insert( tick3 );
session.halt();
session.dispose();
尽管应该避免同时使用fireAllRules()和fireUntilHalt()调用,特别是来自不同线程的调用,但是Drools引擎可以使用线程安全逻辑和内部状态机安全地处理这种情况。如果正在调用一个fireAllRules(),并且您调用了fireUntilHalt(),那么Drools引擎将继续以被动模式运行,直到fireAllRules()操作完成,然后以主动模式启动以响应fireUntilHalt()调用。
Fact在Drools引擎中的传播模式
- Lazy模式:
默认模式。批处理方式。
fact的处理顺序不能保证和输入顺序相同。 - Immediate:
fact在输入的时候,立即传播出去。可以保证顺序性。 - Eager:
在rule执行之前,批量传播。
可以通过@Propagation()改变单个rule的传播模式。type可以是LAZY, IMMEDIATE, 或者 EAGER。
query Q (Integer i)
String( this == i.toString() )
end
rule "Rule" @Propagation(IMMEDIATE)
when
$i : Integer()
?Q( $i; )
then
System.out.println( $i );
end
议程估算过滤器
调用fireAllRules()的时候,可以指定过滤器,过滤哪些rule执行。
ksession.fireAllRules( new RuleNameEndsWithAgendaFilter( "Test" ) );
默认过滤器包括名称匹配,名称前缀匹配,名称后缀匹配等。
DRL rule集合中的rule unit
rule unit 是数据源,全局变量,DRL rule的组,为了特定的目的组织起来。
rule unit是对agenda group以及activation group的加强。
定义rule unit:
package org.mypackage.myunit;
public static class AdultUnit implements RuleUnit {
private int adultAge;
private DataSource<Person> persons;
public AdultUnit( ) { }
public AdultUnit( DataSource<Person> persons, int age ) {
this.persons = persons;
this.age = age;
}
// A data source of `Persons` in this rule unit:
public DataSource<Person> getPersons() {
return persons;
}
// A global variable in this rule unit:
public int getAdultAge() {
return adultAge;
}
// Life-cycle methods:
@Override
public void onStart() {
System.out.println("AdultUnit started.");
}
@Override
public void onEnd() {
System.out.println("AdultUnit ended.");
}
}
persons是Person类型fact的集合。整个rule unit的数据源。
adultAge是一个全局变量,所有的rule都可以引用。
onStart() onEnd()是rule unit的生命周期接口。
Method | Invoked when |
---|---|
onStart() | Rule unit execution starts |
onEnd() | Rule unit execution ends |
onSuspend() | Rule unit execution is suspended (used only with runUntilHalt()) |
onResume() | Rule unit execution is resumed (used only with runUntilHalt()) |
onYield(RuleUnit other) | The consequence of a rule in the rule unit triggers the execution of a different rule unit |
默认,所有的rule都在DRL文件同名的rule unit下。如果DRL文件的包路径和名字,和一个实现了Rulenit接口的类相同,那么DRL下所有的rule都会加入这个unit。
可以通过unit关键字更改unit的名字。
package org.mypackage.myunit
unit AdultUnit
rule Adult
when
$p : Person(age >= adultAge) from persons
then
System.out.println($p.getName() + " is adult and greater than " + adultAge);
end
可以通过更简单的方式OOPath访问数据源。
package org.mypackage.myunit
unit AdultUnit
rule Adult
when
$p : /persons[age >= adultAge]
then
System.out.println($p.getName() + " is adult and greater than " + adultAge);
end
Unit执行方法。
// Create a `RuleUnitExecutor` class and bind it to the KIE base:
KieBase kbase = kieContainer.getKieBase();
RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase );
// Create the `AdultUnit` rule unit using the `persons` data source and run the executor:
RuleUnit adultUnit = new AdultUnit(persons, 18);
executor.run( adultUnit );
除了创建一个Rule Unit的实例,也可以通过绑定的方式传入数据。
executor.bindVariable( "persons", persons );
.bindVariable( "adultAge", 18 );
executor.run( AdultUnit.class );
可以通过注解@UnitVar来更改绑定接口中的key。
package org.mypackage.myunit;
public static class AdultUnit implements RuleUnit {
@UnitVar("minAge")
private int adultAge = 18;
@UnitVar("data")
private DataSource<Person> persons;
}
executor.bindVariable( "data", persons );
.bindVariable( "minAge", 18 );
executor.run( AdultUnit.class );