Drools规则引擎介绍及实践

9 篇文章 0 订阅
1 篇文章 0 订阅

1.规则引擎

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

复杂企业级项目运营及维护过程中随外部条件不断变化的业务规则(business logic),
迫切需要分离商业决策者的商业决策逻辑和应用开发者的技术决策,
并把这些商业决策放在中心数据库或其他统一的地方,让它们能独立运行;可以动态地管理和修改规则模板从而提供软件系统的柔性和适应性

2.Drools规则引擎

Drools具有一个易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。

Drools是为Java量身定制的基于Charles Forgy的RETE算法的规则引擎的实现。支持Java代码直接嵌入到规则文件中,使得商业规则有了更自然的表达。

Drools主要分为两个部分:一是Drools规则,二是Drools规则的解释执行。规则的编译与运行要通过Drools 提供的相关API 来实现。而这些API 总体上游可分为三类:规则编译、规则收集和规则的执行

3.Drools规则引擎优点

3.1申明式编程

明确规则机制让我们可以“做什么”

规则机制的核心优势在于可以简化对于复杂问题的逻辑表述,并对这些逻辑进行验证。

规则机制提供一个如何解决问题的说明,并说明每个决策的是如何得出的

3.2业务逻辑和数据分离

业务数据存储业务对象中,业务决策存储规则中,独立运行

3.3速度和可扩展性

使用网络算法(Rete algorithm),跳跃算法(Leaps algorithm),提供了非常高效的方式根据业务对象的数据匹配规则

3.3.1 Rete算法

Rete算法是一个快速的模式匹配算法,它通过存储关于规则的信息而获得速度

Rete算法的基本思想是保存过去匹配过程中留下的全部信息,以空间代价来换取执行效率 。对每一个模式 ,附加一个匹配元素表来记录WorkingMemory中所有能与之匹配的元素。当一个新元素加入到WorkingMemory时, 找出所有能与之匹配的模式, 并将该元素加入到匹配元素表中; 当一个无素从WorkingMemory中删除时,同样找出所有与该元素匹配的模式,并将元素从匹配元素表中删除。 Rete算法接受对工作存储器的修改操作描述 ,产生一个修改冲突集的动作

Rete的高效率主要来自两个重要的假设

时间冗余性:facts在推理过程中的变化是缓慢的, 即在每个执行周期中,只有少数的facts发生变化,因此影响到的规则也只占很小的比例。所以可以只考虑每个执行周期中已经匹配的facts.
结构相似性:许多规则常常包含类似的模式和模式组。

 

Rete算法的步骤如下:
1.将初始数据(fact)输入Working Memory。
2.使用Pattern Matcher比较规则(rule)和数据(fact)。
3.如果执行规则存在冲突(conflict),即同时激活了多个规则,将冲突的规则放入冲突集合。
4.解决冲突,将激活的规则按顺序放入Agenda。
5.使用规则引擎执行Agenda中的规则。

重复步骤2至5,直到执行完毕所有Agenda中的规则

3.3.2 Leaps 算法


前向推理引擎,包括LEAPS,都包括了匹配-选择-执行(match-select-action)循环。即,确定可以匹配的规则,选择某个匹配 的元 组,此元组相应的规则动作被执行。重复这一过程,直到某一状态(如没有更多的规则动作)。RETE和TREAT匹配算法速度慢的原因是,它们把满足规则条 件的元组都实例化。Leaps算法的最大的改进就是使用一种"lazy"的方法来评估条件(conditions),即仅当必要时才进行元组的实例化。这一改进极大的减少了前向推理引擎的时空复杂度,极大提高了规则执行速度。

Leaps算法将所有的 asserted 的 facts ,按照其被 asserted 在 Working Memory 中的顺序( FIFO ),放在主堆栈中。它一个个的检查 facts ,通过迭代匹配 data type 的 facts 集合来找出每一个相关规则的匹配。当一个匹配的数据被发现时,系统记住此时的迭代位置以备待会的继续迭代,并且激发规则结果( consequence )。当结果( consequence )执行完成以后,系统就会继续处理处于主堆栈顶部的 fact 。如此反复。
Leaps算法的效率可以比Rete算法和Tread算法快几个数量级

3.4规则集中化

通过使用规则,可以创建出一个可运行的知识库。这就意味着对于业务规则可以具备良好的可阅读性,可以起到文档的作用

3.5易懂的规则

通过模型对象以及模型说明语言(Domain Specific Languages)能让你使用很接近自然语言的方式为领域问题建模。借助于这些方式,可让非技术领域的业务使用来描述业务问题

4.Drools规则模板

package rules;                                                // 规则包名路径

import com.xxx.vo.ProductManageVo            // 引入类
import com.xxx.BackList
import com.xxx.service.ProductRiskService
import java.util.HashMap
import java.util.Map

dialect "java"                                                // 定于语言,标识规则中的代码表达式
global ProductRiskService productRiskService                  // 引入外部服务

rule "1001_product"                                           // 规则名称,全局唯一
    salience 1001                                             // 规则优先级,值越大越先执行
    lock-on-active true                                       // 事件是否重复执行该规则 true 至执行一次
    when                                                      // 条件判断关键字
        event:ProductManageVo()                               // 条件判断,是否需要进入action-判断事件对象是否是ProductManageVo对象并赋值于event
    then                                                      // 执行事件关键字
        if(event.getThirdPrice().doubleValue()/event.getTopPrice() <= 0.5){  // 事件业务逻辑,支持java编码
            Map<String,Object> ruleContent = new HashMap<>();
            ruleContent.put("thirdPrice",event.getThirdPrice().doubleValue());
            ruleContent.put("topPrice",event.getTopPrice());
            ruleContent.put("message","三方价格小于TOP原价5");
            productRiskService.saveProductRiskInfo("1001_product",event,ruleContent);
        }
    end                                                           // 规则结束关键字

4.1无约束匹配模式

   rule "1002"
    when
        ProductManageVo()
    then
        System.out.println("无约束的匹配模式,匹配ProductManageVo对象即可");
    end

4.2有条件匹配模式

rule "1003"
    when
        ProductManageVo(topPrice>0)
    then
        System.out.println("有条件匹配模式,ProductManageVo.topPrice>0即可");
    end

4.3匹配并绑定属性模式

rule "1004"
    when
        event:ProductManageVo($topPrice : topPrice>0)
    then
        System.out.println("有条件匹配模,ProductManageVo.topPrice>0;" +"并赋值属性:"

+event.getBarcode()+",$topPrice="+$topPrice);
    end

5.Drools支持的条件约束

rule "1005"
    when
        ProductManageVo(thirdPrice!=null&&topPrice>0)
    then
        System.out.println("条件约束表达式");
    end

约束

描述

!.

使用此运算符可以以空安全的方式取消引用属性。!.运算符左侧的值不能为null(解释为!= null)

[]

按List索引访问值或Map按键访问值

<,<=,>,>=

在具有自然顺序的属性上使用这些运算符

==, !=

在约束中使用这些运算符作为equals()和!equals()方法

&&,||

组合关系条件

matches,not matches

使用这些运算符可以指示字段与指定的Java正则表达式匹配或不匹配

contains,not contains

使用这些运算符可以验证Array或字段是否包含或不包含指定值

memberOf,not memberOf

使用这些运算符可以验证字段是否为定义为变量Array的成员

soundslike

使用英语发音来验证单词是否具有与给定值几乎相同的声音(类似于该matches运算符)

in,notin

使用这些运算符可以指定一个以上的可能值来匹配约束(复合值限制)

6.Drools集合应用

6.1从集合中取数据

rule "1006"
    when
        $event:ProductManageVo()
        $backList : BackList(type==0) from $event.backLists
    then
       System.out.println("遍历集合数据:"+$backList);
    end

 

7.SpringBoot引入Drools.jar

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>6.4.0.Final</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.kie</groupId>
    <artifactId>kie-ci</artifactId>
    <version>6.4.0.Final</version>
</dependency>

8.Java Demo

RiskConfig表结构

CREATE TABLE `r_risk_config` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `risk_code` varchar(128) NOT NULL COMMENT '规则编码',
  `risk_name` varchar(256) DEFAULT NULL COMMENT '规则名称',
  `risk_content` text COMMENT '规则模板',
  `risk_key` varchar(64) DEFAULT NULL COMMENT '规则模板md5,验证是否需要重新加载',
  `risk_type` tinyint(3) DEFAULT '0' COMMENT '0-商品,1-订单',
  `risk_level` tinyint(3) DEFAULT '0' COMMENT '风控等级',
  `is_send_email` tinyint(3) DEFAULT '0' COMMENT '是否发送邮件0-否,1-是',
  `is_wx_news` tinyint(3) DEFAULT '1' COMMENT '是否发送告警信息0-否,1-是',
  `is_fuse` tinyint(3) DEFAULT '0' COMMENT '是否熔断流程0-否,1-是',
  `is_black_list` tinyint(3) DEFAULT '0' COMMENT '是否添加黑名单0-否,1-是',
  `status` tinyint(3) NOT NULL DEFAULT '1' COMMENT '是否生效0-否,1-是',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `creator` bigint(20) DEFAULT NULL COMMENT '创建人',
  `modifier` bigint(20) DEFAULT NULL COMMENT '修改人',
  `modify_time` datetime DEFAULT NULL COMMENT '修改时间',
  `dr` tinyint(1) DEFAULT '0' COMMENT '删除标志',
  `ver` int(11) DEFAULT '1' COMMENT '版本号',
  `ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '时间戳',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_risk_code` (`risk_code`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 COMMENT='风控规则表';

规则模板RiskConfig配置

 

@Service
@Slf4j
public class KieServiceImpl implements KieService {

    private StatelessKieSession kieSession;

    private KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();

    @Resource
    private RiskConfigService riskConfigService;
    //商品规则处理
    @Resource
    private ProductRiskService productRiskService;

    @Override
    public void loadRules(String ruleCode) {
        RiskConfig riskConfig = riskConfigService.queryRiskConfig(ruleCode);
        if(riskConfig!=null){
            loadRule(ruleCode,riskConfig.getRiskContent());
        }
    }

    /**
     * 单一规则下线
     *
     * @param ruleName
     */
    @Override
    public void removeRule(String ruleName) {
        if (kbase.getRule("rules", ruleName) != null) {
            kbase.removeRule("rules", ruleName);
            log.info("remove rule: rules." + ruleName);
            kieSession = kbase.newStatelessKieSession();
            printRules();
        } else {
            log.error("no rule: rules." + ruleName);
        }
    }

    /**
     * 规则引擎执行
     *
     * @param object
     */
    @Override
    public void execute(Object object) {
        this.kieSession.execute(object);
    }

    /**
     * drools全局服务变量
     */
    @PostConstruct
    public void init() {
        loadRulesAll();
    }

    /**
     * @description: 加载所有规则模板
     * @param:
     * @return:
    */
    private void loadRulesAll() {
        RiskConfig config = new RiskConfig();
        List<RiskConfig> riskConfigList = riskConfigService.queryRiskConfigs(config);
        if(riskConfigList!=null){
            for(RiskConfig riskConfig : riskConfigList){
                loadRule(riskConfig.getRiskCode(),riskConfig.getRiskContent());
            }
        }
    }

    /**
     * 单一规则上线
     *
     * @param ruleCode
     * @param ruleContent
     */
    private void loadRule(String ruleCode,String ruleContent){
        try {
            KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
            kbuilder.add(ResourceFactory.newByteArrayResource(ruleContent.getBytes()), ResourceType.DRL);
            if(kbuilder.hasErrors()){
                log.error("加载规则模板引擎异常{},{}",ruleCode,kbuilder.getErrors());
                return;
            }
            kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
            kieSession = kbase.newStatelessKieSession();
            printRules();
            setGlobal();
        }catch (Exception e){
            log.error("加载规则模板引擎异常",e);
        }
    }

    /**
     * @description: 设置全区实现对象
     * @param:
     * @return:
    */
    private void setGlobal(){
        kieSession.setGlobal("productRiskService", productRiskService);
    }

    /**
     * 打印规则
     */
    private void printRules() {
        log.info("print rule start: ---------");
       kbase.getKnowledgePackages().forEach(knowledgePackage ->
                knowledgePackage.getRules().forEach(rule ->
                        log.info("print rule: " + knowledgePackage.getName() + "." + rule.getName())));
        log.info("print rule end: ---------");
    }
}

  • 17
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值