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是一致.

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