使用Drools规则引擎时,很多初学者都会认为Drools的效果比较低,做一个例子,哪怕是写一些简单的规则也会要很多秒的时间,在如今的高并发下,要求的都是效果第一,其实这是初学者的一个误区,在实际开发过程,我们都是要进行优化的,小编就针对Drools优化做一个说明,具体的优化可从两个方向着手,第一:规则语法;第二:执行规则。怎么说呢,第一种方式我们好理解,“规则语法”,就是我们通常所说的Drl文件里的内容,我们知道,每一个规则就是一个if,我们也非常清楚在JAVA语法中AND的优化级是大于OR的,如果遇到有OR的情况,我们都会分成两个IF的方式,在DRL语法也是这样的,举个例子说明:
未优化的Drl:
package rules
import pojo.Rulecondition;
import pojo.Person;
rule Goodsdetail12
no-loop true
when
$r:Rulecondition(moneyMin>=100) OR $p: Person(name== "张三")
then
。。。。。
end
优化的Drl:
package rules
import pojo.Rulecondition;
import pojo.Person;
rule Goodsdetail12
no-loop true
when
$r:Rulecondition(moneyMin>=100)
then
End
rule Goodsdetail13
no-loop true
when
$p: Person(name== "张三")
then
End
WHEN优化第二种方式:
未优化的Drl:
package rules
import pojo.Person;
rule Goodsdetail12
no-loop true
when
$p: Person(age>=50,name=="张三")
then
。。。。。
end
读者看看,上面的规则有什么不妥嘛?很简单,这个道理与查询数据库是一样的,等于查询的数据是要比于区间的,所有,这里我们可以这样进行改造。
package rules
import pojo.Person;
rule Goodsdetail12
no-loop true
when
$p: Person(name=="张三",age>=50)
then
。。。。。
end
规则语法的整合优化:
为何要说规则语法的整合优化呢,其实规则在执行时,会有一个类似预处理的过程,也就是小编之前所说的,规则只执行满足条件的部分(如果使用accumulate可以测试),而规则并不是怕规则多,其实规则多少对整合性能影响并不是很大,只是在生成规则库时需要大量的时间。所有优化规则语法的原则为:
- 规则拆分原则:将规则进行拆分,避免出现OR的情况
- 规则比较原则:将区间或模糊查询的方式排在比较值的后面
- 规则简单原则:尽量避免出现过于复杂比较值
- 规则结果原则:then中避免出来if eles
说完语法的优化,那我们再来说一下执行规则的优化吧,这也是很多初学者很容易出的问题,执行规则,就不得不说Kiesession,我们知道,规则的一切操作离不开kiesession会话,而kiesession会话离不开规则库(Kiebase),大家一般在创建时,使用的都是kmodule.xml文件方式,大部分都直接使用代码:
KieServices kss = KieServices.Factory.get();
KieContainer kc = kss.getKieClasspathContainer();
KieSession ks =kc.newKieSession("session");
我们可以做一个测试,这里就发现第三句code是最耗时的,小编在前面章节中介绍过KieContainer。其实我们看源码时,就会发现Kie容器是需要去创建规则库的,一般的写法应该是创建KieContainer创建出KieBase,通过Kiebase创建出Kiesession。那我们找到了问题的点了,也就是创建规则库时是最耗时的。那有什么方法可以解决呢,小编这里就给读者提方案:
如果规则在不变的情况下,我们是可以直接将针对于这一块业务创建的规则库进行内存级缓存,为何小编会这样说,缓存不是应该放在Redis里嘛?Redis放数据是需要序列化的,而KieBase是不能被放到redis中的,换句话说,规则库只能是当前内存级的(这里会衍生一个问题,下面的小编会点到的)。所有我们可以将规则库进行一个封装,做成全局类型的:
public class DroolsAct{
private static KieBase kieBase;
… 下面再写一个创建规则库的方法就好了
}
如果还先再进一步优化,那就再将Kiesession进行内存级缓存,道理与创建规则库是一样的,做成全局类型的:
public class DroolsAct{
private static KieBase kieBase;
private static Kiesession kiesession;
… 下面再写一个创建规则库的方法就好了
}
当然啦,小编这里肯定还有更好的方式了,这也是小编实际项目中写的方式:
封装一个规则库的javaBean
package com.util;
import com.model.work.AddSkuPresent;
import org.kie.api.KieBase;
import org.kie.api.runtime.StatelessKieSession;
import java.util.Date;
import java.util.List;
import static com.util.NewKieBase.rulekieBase;
public class PromoteExecute2 {
private String sillContent;
//业务规则内容:
private String workContent;
//业务Kbase
private KieBase workKbase;
//业务session
private StatelessKieSession workSession;
public String getSillContent() {
return sillContent;
}
public void setSillContent(String sillContent) {
this.sillContent = sillContent;
}
public String getWorkContent() {
return workContent;
}
public void setWorkContent(String workContent) {
this.workContent = workContent;
}
public KieBase getWorkKbase() {
if (this.workKbase == null) {
this.setWorkKbase();
}
return workKbase;
}
public void setWorkKbase() {
this.workKbase = rulekieBase(this.getWorkContent());
}
public StatelessKieSession getWorkSession() {
if (this.workSession == null) {
this.setWorkSession();
}
return workSession;
}
public void setWorkSession() {
if (null != this.getWorkKbase()) {
this.workSession = this.getWorkKbase().newStatelessKieSession();
}
}
}
创建规则库方法:
package com.util;
import org.apache.commons.lang3.StringUtils;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.internal.utils.KieHelper;
public class NewKieBase {
//将业务规则写到规则库中
public static KieBase rulekieBase(String rule) {
if(StringUtils.isBlank(rule)){
return null;
}
KieHelper helper = new KieHelper();
KieBase kieBase = null;
try {
helper.addContent(rule,ResourceType.DRL);
kieBase = helper.build();
} catch (Exception e) {
e.printStackTrace();
}
return kieBase;
}
}
将上面的javaBean 进行引用就可以了。
后续优化待定…
记得小编说Kiebase只能是内存级的,不能进行缓存,会衍生出一个问题,那就是集群,如何能保证我们在集群应用时,用户调用访问的同一类的规则库呢,小编先将问题提出来,有想法的读者可以给小编发邮件kangzuguan@qq.com, 下期更新教程时,小编会提出自己的想法和解决方案。