Drools学习

1.什么是drools?

Drools是用Java语言编写的开放源码的规则引擎。

那什么是规则引擎呢?参考自 百度百科 里面的定义:

规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。

Drools使用RETE算法对规则进行求值,在Drools6.0(当前最新版本)中还引进了PHREAK算法,Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。Drools 还具有其他优点:

*非常活跃的社区支持
*易用
*快速的执行速度
*在 Java 开发人员中流行
*与 Java Rule Engine API(JSR 94)兼容

2.drools原理是什么?

2.1DRL 解释执行流程

Drools 规则是在 Java 应用程序上运行的,其要执行的步骤顺序由代码确定。为了实现这一点,Drools 规则引擎将业务规则转换成执行树,如下图所示:
在这里插入图片描述
如上图所示,每个规则条件分为小块,在树结构中连接和重用。每次将数据添加到规则引擎中时,它将在与此类似的树中进行求值,并到达一个动作节点,在该节点处,它们将被标记为准备执行特定规则的数据。

2.2规则引擎工作方式

在这里插入图片
1.Pattern Matching:对新的数据被修改的数据进行规则的匹配称为模式匹配.

2.Production Memory:被访问的规则.

3.Agenda:负责具体执行推理算法中被激发规则的结论部分,同时 Agenda 通过冲突决策策略管理这些冲突规则的执行顺序.
Drools 中规则冲突决策策略有
(1) 优先级策略
(2) 复杂度优先策略
(4) 广度策略
(5) 深度策略
(6) 装载序号策略
(7) 随机策略

4.Working Memory:被推理机进行匹配的数据.

5.Inference Engine:进行匹配的引擎称为推理机.
推理机所采用的模式匹配算法有下列几种:
(1) Linear
(2) RETE
(3) Treat
(4) Leaps

进阶内容:Drools规则冲突决策策略Drools匹配算法

2.3drools规则引擎的执行过程

在这里插入图片描述
数据被 assert 进 WorkingMemory 后,和 RuleBase 中的 rule 进行匹配(确切的说应该是 rule 的 LHS ),如果匹配成功这条 rule 连同和它匹配的数据(此时就叫做 Activation )一起被放入 Agenda ,等待 Agenda 来负责安排激发 Activation (其实就是执行 rule 的 RHS ),上图中的菱形部分就是在 Agenda 中来执行的, Agenda 就会根据冲突解决策略来安排 Activation 的执行顺序。

3.为什么要使用drools?

优点:

l声明式编程

l逻辑和数据分离

l速度和可扩展性

知识集中化

缺点:

l复杂性提高

l需要学习新的规则脚本语法

l引入新组件的风险

4.如何应用drools?

4.1.搭建项目环境

//Gradle构建
compile('org.kie:kie-spring:7.15.0.Final')
compile('com.thoughtworks.xstream:xstream:1.4.11.1')

其他可参考官方说明

4.2.HelloWorld用例

在这里插入图片描述
kmodule.xml

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="rules" packages="rules">
        <ksession name="myAgeSession"/>
    </kbase>
</kmodule>

template.drl


package com.hisun.lemon.mkt.rules;
import com.hisun.lemon.mkt.bo.MessageBO;

rule "Hello World"
when
   m : MessageBO( status == MessageBO.HELLO, message : message )
then
   System.out.println( message );
   m.setMessage( "Goodbye cruel world" );
   m.setStatus( MessageBO.GOODBYE );
   update( m );
end

rule "GoodBye"
no-loop true
when
   m : MessageBO( status == MessageBO.GOODBYE, message : message )
then
   System.out.println( message );
   m.setStatus(MessageBO.GAME_OVER);
   m.setMessage("game over now!");
   update( m );
end

rule "game over"
when
   m : MessageBO( status == MessageBO.GAME_OVER)
then
   System.out.println( m.getMessage() );
end

impl

    private static KieContainer container = null;
    private KieSession statefulKieSession = null;
    @Override
    public void helloWorld(MessageBO messageBO) {
        KieServices kieServices = KieServices.Factory.get();
        container = kieServices.getKieClasspathContainer();
        statefulKieSession = container.newKieSession("myAgeSession");

        statefulKieSession.insert(messageBO);
        Integer count=statefulKieSession.fireAllRules();
        statefulKieSession.dispose();
    }
package com.hisun.lemon.mkt.bo;

public class MessageBO {
    public static final int HELLO = 0;
    public static final int GOODBYE = 1;
    public static final int GAME_OVER = 2;

    private String message;

    private int status;

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public int getStatus() {
        return this.status;
    }

    public void setStatus( int status ) {
        this.status = status;
    }
}
输入:
{
  "body": {
    "message": "Hi Zain",
    "status": 0
  }
}
输出:
Hi Zain
Goodbye cruel world
game over now!

也可参考官方说明

4.3.语法说明

Drools语法官方文档

Drools简书

4.4.具体项目体现

动态加载规则:不直接写drl规则文件,从数据库中读取数据,然后拼接成字符串,再加载到work memory中。下面是执行代码:

KieConfiguration

package com.hisun.lemon.mkt.config;

import com.hisun.lemon.mkt.utils.KieUtils;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.*;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;

/**
 * @author
 * @create 2019-01-03 6:57 PM
 * 此类主要用来初始化Drools的配置,其中需要注意的是对KieContainer和KieSession的初始化之后都将其设置到KieUtils类中。
 **/

@Configuration
public class KieConfiguration {

    private static final String RULES_PATH = "rules/";

    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        for (Resource file : getRuleFiles()) {
            kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
        }
        return kieFileSystem;
    }

    private Resource[] getRuleFiles() throws IOException {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
    }

    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        final KieRepository kieRepository = getKieServices().getRepository();

        kieRepository.addKieModule(new KieModule() {
            public ReleaseId getReleaseId() {
                return kieRepository.getDefaultReleaseId();
            }
        });

        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();

        KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
        KieUtils.setKieContainer(kieContainer);
        return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
    }

    private KieServices getKieServices() {
        return KieServices.Factory.get();
    }

    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }

    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException {
        KieSession kieSession = kieContainer().newKieSession();
        KieUtils.setKieSession(kieSession);
        return kieSession;
    }

    @Bean
    @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

    @RedisCacheable(cacheNames = "${lemon.cache.cacheName.prefix}.RuleFile", key = "#ruleVer")
    String getRule(Integer ruleVer) {
        List<MktCouponRuleDO> couponRules = couponRuleDao.findAll(RuleTypEnum.SendCoupon.getValue());
        String ruleHeader = "package com.hisun.lemon.mkt.rule;\ndialect \"java\";\n\nimport com.hisun.lemon.mkt.bo.RuleParamReqBO;\n"
                + "import com.hisun.lemon.mkt.dao.IMktUserCouponDao;\n"
                + "import com.hisun.lemon.mkt.dao.IMktCouponTypeDao;\n"
                + "import com.hisun.lemon.mkt.utils.SummaryUtils;\n\n"
                + "global IMktUserCouponDao userCouponDao;\n"
                + "global IMktCouponTypeDao couponTypeDao\n"
                + "global SummaryUtils summaryUtils\n";
        if (JudgeUtils.isEmpty(couponRules)) {
            //没有生效的营销活动
            LemonException.throwBusinessException("MKT10006");
        }
        StringBuffer ruleBody = getCouponRule(couponRules);
        StringBuffer rule = new StringBuffer(ruleHeader).append(ruleBody);

        //打印规则明细
        logger.info("ruleFile:\n" + rule);

        return rule.toString();
    }

    StringBuffer getCouponRule(List<MktCouponRuleDO> couponRules) {
        String couponId = "";
        String categoryCode = "";
        StringBuffer rule = new StringBuffer();
        String ruleEnd = "msg.addCouponIdList(\"";
        while (couponRules.size() != 0) {
            MktCouponRuleDO couponRule = couponRules.get(0);
            String tempCouponId = couponRule.getCouponId();
            String ruleOperater = couponRule.getRuleOperater();

            if (!couponId.equals("")) {
                if (!couponId.equals(tempCouponId)) {
                    rule = rule.append(")")
                            .append(getActRule(couponId))
                            .append(");\nthen\n")
                            .append(ruleEnd)
                            .append(couponId)
                            .append("\");\n")
                            .append("end\n");
                }
            }

            //处理规则命名
            if (!tempCouponId.equals(couponId)) {
                categoryCode = "";
                rule = rule.append("\nrule \"coupon_")
                        .append(couponRule.getRuleTyp())
                        .append("_")
                        .append(tempCouponId)
                        .append("\"\nwhen\nmsg:RuleParamReqBO(");
            }

            //处理规则条件部分
            String tempCategoryCode = couponRule.getCategoryCode();
            if (!categoryCode.equals("")) {
                if (tempCategoryCode.equals(categoryCode)) {
                    rule = rule.append("||");
                } else {
                    rule = rule.append(")&&");
                }
            } else {
                rule = rule.append("(msg.getIsUseCoupon() == ")
                        .append(convertRuleType(couponRule.getRuleTyp()))
                        .append(")&&");
            }

            if (!rule.substring(rule.length() - 2).equals("||")) {
                rule = rule.append("(");
            }

            rule = rule.append("msg.getReqParam(\"")
                    .append(couponRule.getRuleParam())
                    .append("\")");

            if (ruleOperater.equals("in")) {
                rule = rule.append(" memberOf ")
                        .append(gson.toJson(couponRule.getRuleValue().split(",")));
            } else if (ruleOperater.equals("bt")) {
                rule = rule.append(">=")
                        .append(couponRule.getRuleValue().split(",")[0])
                        .append("&&msg.getReqParam(\"")
                        .append(couponRule.getRuleParam())
                        .append("\")<=")
                        .append(couponRule.getRuleValue().split(",")[1]);
            } else {
                rule = rule.append(couponRule.getRuleOperater())
                        .append(couponRule.getRuleValue());
            }


            //处理规则结果部分
            if (couponRules.size() == 1) {
                rule = rule.append(")")
                        .append(getActRule(tempCouponId))
                        .append(");\nthen\n")
                        .append(ruleEnd)
                        .append(tempCouponId)
                        .append("\");\n")
                        .append("end\n");
            }

            categoryCode = tempCategoryCode;
            couponId = tempCouponId;

            couponRules.remove(couponRule);
        }

        return rule;
    }

缓存规则不影响速度

5.应用场景

风控系统(规则很多,而且容易变动,做互联网金融的同志深有体会)
活动营销系统(活动很多种,集积分送礼品,抽奖送礼品,竞争成功送礼品等等不同形式,这里可以变成不同的规则)
商品折扣系统(同一个商品,不同的用户,每个用户有不同的折扣优惠)
积分系统

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值