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