在maven的pom.xml文件,引入下面依赖:
<properties> <drools.version>7.6.0.Finaldrools.version> properties>
<dependency>
<groupId>org.kiegroupId>
<artifactId>kie-apiartifactId>
<version>${drools.version}version>
dependency>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-coreartifactId>
<version>${drools.version}version>
dependency>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-compilerartifactId>
<version>${drools.version}version>
dependency>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-decisiontablesartifactId>
<version>${drools.version}version>
dependency>
<dependency>
<groupId>org.droolsgroupId>
<artifactId>drools-templatesartifactId>
<version>${drools.version}version>
dependency>
在工程src/main/resources/META-INF/kmodule.xml 文件中配置规则信息;
若kmodule.xml不存在新建,编译器会根据此文件进行规则编译。
配置内容如下:
说明:
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则可能创建多次
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
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;
......
}
1.StatelessKieSession无状态调用
public class StatelessKieSessionDroolsRuleTest {
public static void main(String[] args) {
invokeSimpleRowStatelessKieSessionDroolsRule();
System.out.println("----------------------------------");
invokeMoreRowStatelessKieSessionDroolsRule();
}
/**
* 有会话状态-多行调用规则
*/
private static void invokeMoreRowStatelessKieSessionDroolsRule(){
//执行规则
List 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 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更新引起引擎再次启动检查规则,造成死循环。
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是一致.