当前位置: 首页 > 工具软件 > Drools Flow > 使用案例 >

Drools记录之Drools引擎

丁慈
2023-12-01

一、Drools引擎

        KIE会话可以是无状态的也可以是有状态的。在无状态KIE会话中,来自KIE会话的先前调用(先前的会话状态)的数据在会话调用之间被丢弃。在有状态的KIE会话中,将保留该数据。您使用的KIE会话的类型取决于您的项目要求以及您希望如何保留来自不同资产调用的数据。

1、创建无状态KIE会话

  • 创建KIE容器,此代码编译在类路径上找到的所有规则文件,并将此编译的结果(一个KieModule对象)添加到中KieContainer
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
  • 实例化无状态KIE会话并输入数据,StatelessKieSession从实例化该对象,KieContainer并针对指定的数据执行该对象
StatelessKieSession kSession = kContainer.newStatelessKieSession();

Applicant applicant = new Applicant("Mr John Smith", 16);
assertTrue(applicant.isValid());
ksession.execute(applicant);
assertFalse(applicant.isValid());

2、创建有状态KIE会话

需要注意Memory Leak导致的OOM问题

  • 创建KIE容器
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
  • 实例化有状态的KIE会话并输入数据
KieSession ksession = kContainer.newKieSession();

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();

有状态KIE会话要销毁,调用ksession.dispose();

KIE会话池

  • KIE会话池
// Obtain a KIE session pool from the KIE container
KieContainerSessionsPool pool = kContainer.newKieSessionsPool(10);

// Create KIE sessions from the KIE session pool
KieSession kSession = pool.newKieSession();

3、Drools引擎详细

(1)Drools规则文件中插入fact

声明的插入:用定义insert()
逻辑插入:用定义insertLogical(),逻辑插入后,如果插入事实的规则中的条件不再成立,则插入的事实将自动撤回。当没有条件支持逻辑插入时,事实将撤回。

rule "Issue Child Bus Pass"
when
  $p : Person(age < 18)
then
  insert(new ChildBusPass($p));
end

rule "Issue Adult Bus Pass"
when
  $p : Person(age >= 18)
then
  insert(new AdultBusPass($p));
end

(2)Drools引擎中的平等模式

identity:(默认)Drools引擎使用IdentityHashMap来存储所有插入的事实。对于每个新事实插入,Drools引擎都会返回一个新FactHandle对象。如果再次插入事实,则Drools引擎将返回原始FactHandle对象,而忽略针对同一事实的重复插入。在这种模式下,两个事实对于Drools引擎来说是相同的,只有它们是具有相同标识的完全相同的对象。

equality:Drools引擎使用a HashMap来存储所有插入的事实。根据FactHandle插入事实的equals()方法,仅当插入事实与现有事实不相等时,Drools引擎才会返回新对象。在这种模式下,如果Drools引擎以相同的方式构成两个事实,而与身份无关,则这两个事实是相同的。当您希望基于功能等同性而非显式身份评估对象时,请使用此模式。

  • 以编程方式创建KIE基础时,设置equality模式
KieServices ks = KieServices.get();
KieBaseConfiguration kieBaseConf = ks.newKieBaseConfiguration();
kieBaseConf.setOption(EqualityBehaviorOption.EQUALITY);
KieBase kieBase = kieContainer.newKieBase(kieBaseConf);

(3)Drools引擎规则执行先后控制
       每个规则都有一个整数salience属性,该属性确定执行顺序。在激活队列中排序时,具有较高显着性值的规则将具有较高的优先级。规则的默认显着性值为零,但显着性可以为负或正。

例如,以下示例DRL规则按所示顺序在Drools引擎堆栈中列出:

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
该RuleB规则列在第二位,但其显着性值高于该RuleA规则,因此首先执行。

(4)Drools引擎议程小组 --agenda-group
       议程将分区规则分组在Drools引擎议程上。在任何时候,只有一个小组关注的重点是使该小组规则优先于其他议程组中的规则执行。您可以通过setFocus()呼叫议程组来确定焦点。您还可以使用auto-focus属性定义规则,以便下次激活该规则时,焦点将自动分配给分配了该规则的整个议程组。

  • 为议程组的执行顺序设置焦点
Agenda agenda = ksession.getAgenda();
agenda.getAgendaGroup( "report" ).setFocus();
agenda.getAgendaGroup( "calculation" ).setFocus();
ksession.fireAllRules();
银行应用程序的示例DRL规则
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"议程组中的规则,
然后始终必须执行议程组中的规则。然后可以执行其他议程组中的所有其余规则。
因此,"report"和"calculation"组必须先接收要按此顺序执行的焦点,然后才能执行其他规则
  • 取消其他规则
ksession.getAgenda().getAgendaGroup( "Group A" ).clear();

(5)Drools引擎规则激活组 --activation-group
       激活组是由相同activation-group规则属性绑定在一起的一组规则。在该组中,只能执行一个规则。在满足该组中要执行的规则的条件后,该激活组中所有其他待执行的规则将被从议程中删除。

银行业DRL规则样本
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
对于此示例,如果"report"执行了激活组中的第一条规则,
则将从议程中删除该组中的第二条规则以及该议程上的所有其他可执行规则。

(6)Drools引擎中的规则执行模式和线程安全
              --控制Drools引擎执行规则的方式和时间

  • 被动模式:(默认)当用户或应用程序显式调用时,Drools引擎将评估规则fireAllRules()。Drools引擎中的被动模式最适合需要直接控制规则评估和执行的应用程序,或者是使用Drools引擎中使用伪时钟实现的复杂事件处理(CEP)应用程序的最佳选择。

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(),Drools引擎将在活动模式下启动并持续评估规则,直到用户或应用程序明确调用halt()。Drools引擎中的活动模式最适合将规则评估和执行控制权委派给Drools引擎的应用程序,或者最适合使用Drools引擎中的实时时钟实现的复杂事件处理(CEP)应用程序。对于使用活动查询的CEP应用程序,活动模式也是最佳选择。

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();

事实传播模式?

(7)Drools引擎过滤器
以下示例代码仅允许"Test"评估和执行以字符串结尾的规则。所有其他规则均从Drools引擎议程中过滤掉。

ksession.fireAllRules( new RuleNameEndsWithAgendaFilter( "Test" ) );

(8)Drools引擎规则单元
规则单元是为特定目的共同起作用的数据源,全局变量和DRL规则的组。您可以使用规则单元将规则集划分为较小的单元,将不同的数据源绑定到这些单元,然后执行单个单元。规则单元是规则分组DRL属性(例如规则议程组或用于执行控制的激活组)的增强替代方案。
当您要协调规则执行时,规则单元会很有用,这样一个规则单元的完全执行会触发另一个规则单元的启动,依此类推。例如,假定您具有一组用于数据充实的规则,另一组用于处理该数据的规则以及另一组从已处理数据中提取输出的规则。如果将这些规则集添加到三个不同的规则单元中,则可以协调这些规则单元,以使第一个单元的完全执行触发第二个单元的开始,而第二个单元的完全执行触发第三个单元的开始。

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.");
    }
}
方法何时调用
onStart()规则单元开始执行
onEnd()规则单元执行结束
onSuspend()规则单元执行被暂停(仅用于runUntilHalt())
onResume()恢复执行规则单元(仅用于runUntilHalt())
onYield(RuleUnit other)规则单元中规则的结果触发另一个规则单元的执行

可以将一个或多个规则添加到规则单元。默认情况下,DRL文件中的所有规则都会自动与遵循DRL文件名命名约定的规则单元相关联。如果DRL文件与实现RuleUnit接口的类位于相同的程序包中,并且具有相同的名称,则该DRL文件中的所有规则都隐式属于该规则单元。例如,包中AdultUnit.drl文件中的所有规则org.mypackage.myunit自动成为规则单元的一部分org.mypackage.myunit.AdultUnit。
要覆盖此命名约定并显式声明DRL文件中的规则所属的规则单元,请在DRL文件中使用unit关键字。该unit声明必须紧随包声明之后,并包含该包中DRL文件中的规则所属的类的名称。

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

不要在同一KIE库中混合使用规则单元和不使用规则单元的规则。在KIE基础中混合两个规则范例会导致编译错误。

规则单元执行示例

// 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 );

(8)Drools引擎指定异常处理程序

drools.consequenceExceptionHandler=org.drools.core.runtime.rule.impl.MyCustomConsequenceExceptionHandler

KieServices ks = KieServices.Factory.get();
KieBaseConfiguration kieBaseConf = ks.newKieBaseConfiguration(); 
kieBaseConf.setOption(ConsequenceExceptionHandlerOption.get(MyCustomConsequenceExceptionHandler.class));
KieBase kieBase = kieContainer.newKieBase(kieBaseConf);

(9)以编程方式创建KIE基础时启用多线程评估,默认false

KieServices ks = KieServices.Factory.get();
KieBaseConfiguration kieBaseConf = ks.newKieBaseConfiguration();
kieBaseConf.setOption(MultithreadEvaluationOption.YES);
KieBase kieBase = kieContainer.newKieBase(kieBaseConf);

(10)顺序模式,仅适用于无状态KIE会话,默认false
在顺序模式下,Drools引擎按规则在Drools引擎议程中列出的顺序一次评估规则,而不考虑工作内存的变化。这意味着Drools引擎将忽略规则中的任何insert,modify或update语句,并按单个顺序执行规则

KieServices ks = KieServices.Factory.get();
KieBaseConfiguration kieBaseConf = ks.newKieBaseConfiguration();
kieBaseConf.setOption(SequentialOption.YES);
KieBase kieBase = kieContainer.newKieBase(kieBaseConf);

(11)Drools引擎查询和实时查询

引擎查询

query "people under the age of 21"
    $person : Person( age < 21 )
end

获取和迭代查询结果的示例应用程序代码

QueryResults results = ksession.getQueryResults( "people under the age of 21" );
System.out.println( "we have " + results.size() + " people under the age of 21" );

System.out.println( "These people are under the age of 21:" );

for ( QueryResultsRow row : results ) {
    Person person = ( Person ) row.get( "person" );
    System.out.println( person.getName() + "\n" );
}

实时查询

query colors(String $color1, String $color2)
    TShirt(mainColor = $color1, secondColor = $color2, $price: manufactureCost)
end
final List updated = new ArrayList();
final List removed = new ArrayList();
final List added = new ArrayList();

ViewChangedEventListener listener = new ViewChangedEventListener() {
 public void rowUpdated(Row row) {
  updated.add( row.get( "$price" ) );
 }

 public void rowRemoved(Row row) {
  removed.add( row.get( "$price" ) );
 }

 public void rowAdded(Row row) {
  added.add( row.get( "$price" ) );
 }
};

// Open the live query:
LiveQuery query = ksession.openLiveQuery( "colors",
                                          new Object[] { "red", "blue" },
                                          listener );
...
...

// Terminate the live query:
query.dispose()
 类似资料: