Drools规则调用与死循环问题排查

一.drools规则依赖

在maven的pom.xml文件,引入下面依赖:
<properties>
    <drools.version>7.6.0.Final</drools.version>
</properties>

<!--drools-->
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-api</artifactId>
    <version>${drools.version}</version>
</dependency>
<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>drools-decisiontables</artifactId>
    <version>${drools.version}</version>
</dependency>
<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-templates</artifactId>
    <version>${drools.version}</version>
</dependency>

 

二.drools规则编译配置

在工程src/main/resources/META-INF/kmodule.xml 文件中配置规则信息;

若kmodule.xml不存在新建,编译器会根据此文件进行规则编译。

配置内容如下:

<?xml version="1.0" encoding="UTF-8"?>

<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">

    <kbase name="rules_audit" packages="rules.audit">

        <ksession name="ksession-audit" type="stateless"/>

    </kbase>

    <kbase name="rules-sms" packages="rules.sms">

        <ksession name="ksession-sms"/>

    </kbase>

</kmodule>

 

说明:

kmodule: 可包含多个kbase子节点。

kbase.name: 配置kbase节点name是唯一的既不能重名

kbase.packages: 配置drools规则包(*.drl)文件,可定义多个包,用逗号隔开;在src/main/resources下面的文件夹(如:rules.audit表示src/main/resources/rules/audit目录)名称。

ksession.name:配置ksession名称,任意字符串但不能重名,可以有多个。

ksession.type:配置ksession类型,默认为有状态,stateless表示无状态。

 

注意:

在运行时、KieContainer会根据*Model对象来创建KieModule,KieBase,KieSession对象,其中KieModule和KieBase只会创建一次,而KieSession则可能创建多次

 

三.drools规则配置

1) OrderAudit-1.0.0.1.drl规则文件配置:

在src/main/resources/rules/audit目录下创建OrderAudit-1.0.0.1.drl规则文件,内容如下:

package order_audit_setting_rules;

import com.eron.order.OrderAudit

 

// 订单不审核

rule "do not audit"

        salience 1200

        when

            $a:OrderAudit(logisticsStatus not in (0,1))

        then

              modify($a){

                          setOrderStatus( 10004 )

                      }

end

 

// 订单审核拒绝

rule "DECLINE STAY"

     no-loop true

     salience 2450

         when

              $a:OrderAudit(orderStatus not in (1))

         then

         modify( $a ) {

              setAuditMessage("拟审核拒绝不调额"),

              setOrderStatus(1),

              setLogisticsStatus(2)

         }

end

 

2) OrderSms-1.0.0.1.drl规则文件配置:

在src/main/resources/rules/sms目录下创建OrderSms-1.0.0.1.drl规则文件,内容如下:

package order_sms_setting_rules;

import com.eron.order.OrderAudit

 

rule "greater than 2 rule"

when

     $a:UserInfo(userId != null,userId > 2);

then

     $a.setPushType(2);

     $a.setTemplateId("SMS_T10_TEMPLATE");

end

 

rule "other"

when

     $a:UserInfo(userId != null,userId <= 2);

then

     $a.setPushType(1);

     $a.setTemplateId("SMS_T2_TEMPLATE");

End

 

四.drools规则模型

1)OrderAudit-1.0.0.1.drl规则模型

/***

 * 订单审核

 * @date 2019/09/17

 */

public class OrderAudit implements Serializable{

 

    static final long serialVersionUID = 1L;

 

    @Label("用户Id")

    private Long userId;

 

    @Label("订单状态")

    private Integer orderStatus;

 

    @Label("物流状态")

    private Integer logisticsStatus;

 

    @Label("审核信息")

    private String auditMessage;

    

     ......

}

 

2)OrderSms-1.0.0.1.drl规则模型

/***

 * 用户信息

 * @date 2019/09/17

 */

public class UserInfo implements Serializable{

 

    static final long serialVersionUID = 1L;

 

    @Label("用户Id")

    private Long userId;

 

    @Label("模板类型")

    private Integer pushType ;

 

    @Label("模板Id")

    private String templateId;

 

     ......

}

五.drools规则调用

1.StatelessKieSession无状态调用
public class StatelessKieSessionDroolsRuleTest {

 
    public static void main(String[] args) {
        invokeSimpleRowStatelessKieSessionDroolsRule();
        System.out.println("----------------------------------");
        invokeMoreRowStatelessKieSessionDroolsRule();
    }

 
    /**
     * 有会话状态-多行调用规则
     */
    private static void invokeMoreRowStatelessKieSessionDroolsRule(){
        //执行规则
        List<Command> cmds = new ArrayList<>();
        for (int userIndex = 0; userIndex < 4; userIndex++) {
            UserInfo userInfo = new UserInfo();
            userInfo.setUserId(Long.valueOf(userIndex+1));
            System.out.println("规则入参:" + JsonUtil.convertToJson(userInfo));
            cmds.add(CommandFactory.newInsert(userInfo, String.valueOf(userIndex)));
        }
        //填充规则
        cmds.add(CommandFactory.newFireAllRules());
        //执行规则
        ExecutionResults results = getStatelessKieSession().execute(CommandFactory.newBatchExecution(cmds));
        for (int userIndex = 0; userIndex < 4; userIndex++) {
            Object userObject = results.getValue(String.valueOf(userIndex));
            UserInfo userInfo = (UserInfo) userObject;
            System.out.println("规则出参:" + JsonUtil.convertToJson(userInfo));
        }
    }
    /**
     * 有会话状态-单行调用规则
     */
    private static void invokeSimpleRowStatelessKieSessionDroolsRule(){
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(1000L);
        //执行规则
        System.out.println("规则入参:" + JsonUtil.convertToJson(userInfo));
        StatelessKieSession statelessKieSession = getStatelessKieSession();
        statelessKieSession.execute(userInfo);
        System.out.println("规则出参:" + JsonUtil.convertToJson(userInfo));
    }

 
    /**
     * 获取无状态KieSession会话
     * @return
     */
    private static StatelessKieSession getStatelessKieSession(){
        //根据KieServices.Factory工厂,创建KieServices类
        KieServices kieServices = KieServices.Factory.get();
        //从KieServices中,获得KieContainer实例,其会加载kmodule.xml文件与所有DRL规则文件,并将该编译结果KieModule放在KieContainer容器。
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        //建立KieSession到规则文件的通信管道
        StatelessKieSession statelessKieSession = kieContainer.newStatelessKieSession("ksession-sms");
        return statelessKieSession;
    }
}

 

规则结果:

【单行调用】

规则入参:{"userId":1000,"pushType":null,"templateId":null}

规则出参:{"userId":1000,"pushType":2,"templateId":"SMS_T10_TEMPLATE"}

 

【多行调用】

规则入参:{"userId":1,"pushType":null,"templateId":null}

规则入参:{"userId":2,"pushType":null,"templateId":null}

规则入参:{"userId":3,"pushType":null,"templateId":null}

规则入参:{"userId":4,"pushType":null,"templateId":null}

规则出参:{"userId":1,"pushType":1,"templateId":"SMS_T2_TEMPLATE"}

规则出参:{"userId":2,"pushType":1,"templateId":"SMS_T2_TEMPLATE"}

规则出参:{"userId":3,"pushType":2,"templateId":"SMS_T10_TEMPLATE"}

规则出参:{"userId":4,"pushType":2,"templateId":"SMS_T10_TEMPLATE"}

 

2.KieSession有状态调用
public class KieSessionDroolsRuleTest {

 
    public static void main(String[] args) {
        invokeSimpleRowKieSessionDroolsRule();
        System.out.println("----------------------------------");
        invokeMoreRowKieSessionDroolsRule();
    }

 
    /**
     * 有会话状态:多行调用规则
     */
    private static void invokeMoreRowKieSessionDroolsRule(){
        List<Command> cmds = new ArrayList<>();
        for (int auditIndex = 0; auditIndex < 5; auditIndex++) {
            OrderAudit orderAudit = new OrderAudit();
            orderAudit.setUserId(Long.valueOf(auditIndex));
            orderAudit.setOrderStatus(2);
            System.out.println("规则入参:" + JsonUtil.convertToJson(orderAudit));
            cmds.add(CommandFactory.newInsert(orderAudit, String.valueOf(auditIndex)));
        }
        //填充规则
        cmds.add(CommandFactory.newFireAllRules());

 
        //执行规则
        ExecutionResults results = getKieSession().execute(CommandFactory.newBatchExecution(cmds));
        for (int auditIndex = 0; auditIndex < 5; auditIndex++) {
            Object auditObject = results.getValue(String.valueOf(auditIndex));
            OrderAudit orderAudit = (OrderAudit) auditObject;
            System.out.println("规则出参:" + JsonUtil.convertToJson(orderAudit));
        }
    }

 
    /**
     * 有会话状态:单行调用规则
     */
    private static void invokeSimpleRowKieSessionDroolsRule(){
        OrderAudit orderAudit = new OrderAudit();
        orderAudit.setUserId(Long.valueOf(1000));
        orderAudit.setOrderStatus(2);
        System.out.println("规则入参:" + JsonUtil.convertToJson(orderAudit));

 
        //执行规则
        KieSession kieSession = getKieSession();
        kieSession.insert(orderAudit);
        kieSession.fireAllRules();
        System.out.println("规则出参:" + JsonUtil.convertToJson(orderAudit));
    }

 
    /**
     * 获取有状态KieSession会话
     * @return
     */
    private static KieSession getKieSession(){
        //根据KieServices.Factory工厂,创建KieServices类
        KieServices kieServices = KieServices.Factory.get();
        //从KieServices中,获得KieContainer实例,其会加载kmodule.xml文件与所有DRL规则文件,并将该编译结果KieModule放在KieContainer容器。
        KieContainer kieContainer = kieServices.getKieClasspathContainer();
        //建立KieSession到规则文件的通信管道
        KieSession kieSession = kieContainer.newKieSession("ksession-audit");
        return kieSession;
    }
}

 

注意:

1)如果引擎内部因为对Fact更新引起引擎再次启动检查规则,那么它会忽略掉所有的no-loop属性设置为true的规则。

2本案例主要验证"DECLINE STAY"与"do not audit"在对Fact更新引起引擎再次启动检查规则,造成死循环。

六.drools常见问题

1) salience优先级

作用:设置规则执行的优先级,值是一个数字,数字越大执行的优先级越高,它的值可以是一个负数,默认值是0如果我们不手动设置salience属性值,则执行顺序是随机的。

2) no-loop防止死循环

在一个规则中如果条件满足就对Working Memory当中的某个Fact对象进行修改,比如使用update将其更新到当前的Working Memory当中,这时候引擎会再次检查所有的规则是否满足条件,如果满足会再执行,可能会出现死循环

作用:用来控制已经执行过的规则条件再次满足时是否再次执行,默认是false,如果属性值是true,表示该规则只会被规则引擎检查一次,如果满足条件就执行规则的RHS部分。

3)引擎内部因为对Fact更新引起引擎再次启动检查规则,那么它会忽略掉所有的no-loop属性设置为true的规则,造成死循环。

问题:案例"DECLINE STAY"与"do not audit"在对Fact更新引起引擎再次启动检查规则,造成死循环。

排查方法:

1)top:查看机器性能,找到java进程号30411

[root@eronfat026053 ~]# top

top - 14:55:21 up 525 days, 19 min,  2 users,  load average: 0.95, 0.78, 0.87

Tasks: 135 total,   1 running, 128 sleeping,   6 stopped,   0 zombie

%Cpu(s):  0.3 us,  0.3 sy,  0.0 ni, 96.4 id,  0.0 wa,  0.0 hi,  0.0 si,  3.0 st

KiB Mem :  3684076 total,   155468 free,  3333552 used,   195056 buff/cache

KiB Swap:  4194300 total,  3047360 free,  1146940 used.   129496 avail Mem

 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                 

30411 root      20   0 6049216 2.614g   5980 S   2.3 74.4 338:36.87 java                                                                                                                                                                                                    

 5380 root      20   0 1336896  39880   1572 S   1.7  1.1 266:51.01 titanagent                                                                                                                                                                                               

 5382 root       9 -11   87648    792    448 S   1.0  0.0 230:15.12 titan_monitor                                                                                                                                                                                            

   26 root      20   0       0      0      0 S   0.3  0.0 392:33.12 rcuos/0                                                                                                                                                                                                  

  413 root      20   0       0      0      0 S   0.3  0.0 219:49.21 xfsaild/dm-0                                                                                                                                                                                             

    1 root      20   0  392008 265036   1216 S   0.0  7.2 134:43.78 systemd                                                                                                                                                                                                  

    2 root      20   0       0      0      0 S   0.0  0.0   0:04.66 kthreadd                                                                                                                                                                                                 


2)top -Hp 30411:查看java进程中各个线程情况,找到占用cpu最高的java线程号

[root@eronfat026053 ~]# top -Hp 30411

top - 14:57:04 up 525 days, 21 min,  2 users,  load average: 0.61, 0.69, 0.83

Threads: 159 total,   3 running, 156 sleeping,   0 stopped,   0 zombie

%Cpu(s): 20.9 us,  4.1 sy,  0.0 ni, 48.4 id,  0.0 wa,  0.0 hi,  0.2 si, 26.4 st

KiB Mem :  3684076 total,   171700 free,  3329680 used,   182696 buff/cache

KiB Swap:  4194300 total,  3042080 free,  1152220 used.   139536 avail Mem

 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                                                                                                                                                   

30533 root      20   0 6055788 2.616g  12560 R 49.8 74.5 295:44.24 java                                                                                                                                                                                                      

30537 root      20   0 6055788 2.616g  12560 S  8.3 74.5   0:09.93 java                                                                                                                                                                                                      

30419 root      20   0 6055788 2.616g  12560 S  2.7 74.5   2:20.23 java                                                                                                                                                                                                     

30538 root      20   0 6055788 2.616g  12560 S  1.3 74.5   0:09.39 java                                                                                                                                                                                                     

30415 root      20   0 6055788 2.616g  12560 S  1.0 74.5   1:20.05 java                                                                                                                                                                                                     

30535 root      20   0 6055788 2.616g  12560 S  1.0 74.5   0:10.14 java


3)jstack 30533 > /tmp/pid-30533.txt :输出各个线程执行到哪个方法导致的CPU飙升
vi /tmp/ pid-30533.txt 内容如下:

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):

"main" #1 prio=5 os_prio=0 tid=0x000000000207f000 nid=0x3c30 runnable [0x00000000021be000]

   java.lang.Thread.State: RUNNABLE

      at java.lang.Object.notifyAll(Native Method)

      at org.drools.core.phreak.SynchronizedPropagationList.notifyWaitOnRest(SynchronizedPropagationList.java:137)

      - eliminated <0x00000006c1fe9178> (a org.drools.core.phreak.SynchronizedPropagationList)

      at org.drools.core.phreak.SynchronizedPropagationList.internalAddEntry(SynchronizedPropagationList.java:71)

      - locked <0x00000006c1fe9178> (a org.drools.core.phreak.SynchronizedPropagationList)

      at org.drools.core.phreak.SynchronizedPropagationList.addEntry(SynchronizedPropagationList.java:64)

      at org.drools.core.common.DefaultAgenda.addPropagation(DefaultAgenda.java:1260)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.addPropagation(StatefulKnowledgeSessionImpl.java:2052)

      at org.drools.core.reteoo.EntryPointNode.modifyObject(EntryPointNode.java:251)

      at org.drools.core.common.NamedEntryPoint.update(NamedEntryPoint.java:423)

      at org.drools.core.common.NamedEntryPoint.update(NamedEntryPoint.java:413)

      at org.drools.core.base.DefaultKnowledgeHelper.update(DefaultKnowledgeHelper.java:411)

      at order_audit_setting_rules.Rule_DECLINE_STAY1012618649.defaultConsequence(Rule_DECLINE_STAY1012618649.java:11)

      at order_audit_setting_rules.Rule_DECLINE_STAY1012618649DefaultConsequenceInvokerGenerated.evaluate(Unknown Source)

      at order_audit_setting_rules.Rule_DECLINE_STAY1012618649DefaultConsequenceInvoker.evaluate(Unknown Source)

      at org.drools.core.phreak.RuleExecutor.innerFireActivation(RuleExecutor.java:431)

      at org.drools.core.phreak.RuleExecutor.fireActivation(RuleExecutor.java:379)

      at org.drools.core.phreak.RuleExecutor.fire(RuleExecutor.java:135)

      at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:88)

      at org.drools.core.concurrent.AbstractRuleEvaluator.internalEvaluateAndFire(AbstractRuleEvaluator.java:34)

      at org.drools.core.concurrent.SequentialRuleEvaluator.evaluateAndFire(SequentialRuleEvaluator.java:43)

      at org.drools.core.common.DefaultAgenda.fireLoop(DefaultAgenda.java:1067)

      at org.drools.core.common.DefaultAgenda.internalFireAllRules(DefaultAgenda.java:1014)

      at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1006)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1318)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1309)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1293)

      at com.eron.service.KieSessionDroolsRuleTest.invokeSimpleRowKieSessionDroolsRule(KieSessionDroolsRuleTest.java:59)

      at com.eron.service.KieSessionDroolsRuleTest.main(KieSessionDroolsRuleTest.java:18)

 

"VM Thread" os_prio=2 tid=0x000000001bbf4000 nid=0x5cc runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000001f61000 nid=0x4190 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001d911000 nid=0x215c waiting on condition

JNI global references: 420

 

"main" #1 prio=5 os_prio=0 tid=0x000000000207f000 nid=0x3c30 runnable [0x00000000021be000]

   java.lang.Thread.State: RUNNABLE

      at java.lang.Object.notifyAll(Native Method)

      at org.drools.core.phreak.SynchronizedPropagationList.notifyWaitOnRest(SynchronizedPropagationList.java:137)

      - eliminated <0x00000006c1fe9178> (a org.drools.core.phreak.SynchronizedPropagationList)

      at org.drools.core.phreak.SynchronizedPropagationList.internalAddEntry(SynchronizedPropagationList.java:71)

      - locked <0x00000006c1fe9178> (a org.drools.core.phreak.SynchronizedPropagationList)

      at org.drools.core.phreak.SynchronizedPropagationList.addEntry(SynchronizedPropagationList.java:64)

      at org.drools.core.common.DefaultAgenda.addPropagation(DefaultAgenda.java:1260)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.addPropagation(StatefulKnowledgeSessionImpl.java:2052)

      at org.drools.core.reteoo.EntryPointNode.modifyObject(EntryPointNode.java:251)

      at org.drools.core.common.NamedEntryPoint.update(NamedEntryPoint.java:423)

      at org.drools.core.common.NamedEntryPoint.update(NamedEntryPoint.java:413)

      at org.drools.core.base.DefaultKnowledgeHelper.update(DefaultKnowledgeHelper.java:411)

      at order_audit_setting_rules.Rule_do_not_audit754124421.defaultConsequence(Rule_do_not_audit754124421.java:9)

      at order_audit_setting_rules.Rule_do_not_audit754124421DefaultConsequenceInvokerGenerated.evaluate(Unknown Source)

      at order_audit_setting_rules.Rule_do_not_audit754124421DefaultConsequenceInvoker.evaluate(Unknown Source)

      at org.drools.core.phreak.RuleExecutor.innerFireActivation(RuleExecutor.java:431)

      at org.drools.core.phreak.RuleExecutor.fireActivation(RuleExecutor.java:379)

      at org.drools.core.phreak.RuleExecutor.fire(RuleExecutor.java:135)

      at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:88)

      at org.drools.core.concurrent.AbstractRuleEvaluator.internalEvaluateAndFire(AbstractRuleEvaluator.java:34)

      at org.drools.core.concurrent.SequentialRuleEvaluator.evaluateAndFire(SequentialRuleEvaluator.java:43)

      at org.drools.core.common.DefaultAgenda.fireLoop(DefaultAgenda.java:1067)

      at org.drools.core.common.DefaultAgenda.internalFireAllRules(DefaultAgenda.java:1014)

      at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1006)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1318)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1309)

      at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1293)

      at com.eron.service.KieSessionDroolsRuleTest.invokeSimpleRowKieSessionDroolsRule(KieSessionDroolsRuleTest.java:59)

      at com.eron.service.KieSessionDroolsRuleTest.main(KieSessionDroolsRuleTest.java:18)

"VM Thread" os_prio=2 tid=0x000000001bbf4000 nid=0x5cc runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000001f61000 nid=0x4190 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001d911000 nid=0x215c waiting on condition

JNI global references: 420

 

堆栈关键信息:

at order_audit_setting_rules.Rule_do_not_audit754124421.defaultConsequence(Rule_do_not_audit754124421.java:9)

at order_audit_setting_rules.Rule_DECLINE_STAY1012618649.defaultConsequence(Rule_DECLINE_STAY1012618649.java:11)

 

从堆栈关键信息得出如下信息:

order_audit_setting_rules:  得出在drl文件package包路径为order_audit_setting_rules文件中有死锁问题。

Rule_do_not_audit_754124421:得出在drl文件package包路径为order_audit_setting_rules文件的"do not audit"规则名

Rule_DECLINE_STAY1012618649:得出drl文件package包路径为order_audit_setting_rules文件的"DECLINE STAY"规则名

 

注意:

drl编译后编译文件规则:规则文件package路径.Rule_+规则名+编译随机数;如果规则名存在空格,会使用下划线(_)替换.

 

解决方案:

根据package路径order_audit_setting_rules定位到对应drl文件,并对"do not audit"与"DECLINE STAY"规则进行优化。

 

附注:

1)参考:https://www.cnblogs.com/ffaiss/p/10995891.html

2)windows重现问题排查命令:

C:\Users\eronfat>jps

16928 Jps

13796 Launcher

8088 KieSessionDroolsRuleTest

11660

16604 Launcher

 

C:\Users\eronfat >jstack 8088 >>D:\pid.txt

文件内容跟linux是一致.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章