Drools 规则引擎应用 看这一篇就够了

1 .场景1.1需求商城系统消费赠送积分100元以下, 不加分 100元-500元 加100分 500元-1000元 加500分 1000元 以上 加1000分......1.2传统做法1.2.1 if...elseif (order.getAmout() <= 100){order.setScore(0);addScore(order);}else if(order.getAmout() > 100 && order.getAmout() <= 500){order.setScore(100);addScore(order);}else if(order.getAmout() > 500 && order.getAmout() <= 1000){order.setScore(500);addScore(order);}else{order.setScore(1000);addScore(order);}1.2.2 策略interface Strategy {addScore(int num1,int num2);}class Strategy1 {addScore(int num1);}......................interface StrategyN {addScore(int num1);}class Environment {private Strategy strategy;public Environment(Strategy strategy) {this.strategy = strategy;}public int addScore(int num1) {return strategy.addScore(num1);}}1.2.3 问题?以上解决方法问题思考:
如果需求变更,积分层次结构增加,积分比例调整?
数据库?
遇到的问题瓶颈:
第一,我们要简化if else结构,让业务逻辑和数据分离!
第二,分离出的业务逻辑必须要易于编写,至少单独编写这些业务逻辑,要比写代码快!
第三,分离出的业务逻辑必须要比原来的代码更容易读懂!
第四,分离出的业务逻辑必须比原来的易于维护,至少改动这些逻辑,应用程序不用重启!
2.是什么2.1概念规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策 。接受数据输入,解释业务规则,并根据业务规则做出业务决策
需要注意的是规则引擎并不是一个具体的技术框架,而是指的一类系统,即业务规则管理系统 。目前市面上具体的规则引擎产品有:drools、VisualRules、iLog等
在很多企业的 IT 业务系统中,经常会有大量的业务规则配置,而且随着企业管理者的决策变化,这些业务规则也会随之发生更改 。为了适应这样的需求,我们的 IT 业务系统应该能快速且低成本的更新 。适应这样的需求,一般的作法是将业务规则的配置单独拿出来,使之与业务系统保持低耦合 。目前,实现这样的功能的程序,已经被开发成为规则引擎 。
2.2 起源

Drools 规则引擎应用 看这一篇就够了

文章插图
2.3 原理--基于 rete 算法的规则引擎2.3.1 原理在 AI 领域,产生式系统是一个很重要的理论,产生式推理分为正向推理和逆向推理产生式,其规则的一般形式是:IF 条件 THEN操作 。rete 算法是实现产生式系统中正向推理的高效模式匹配算法,通过形成一个 rete网络进行模式匹配,利用基于规则的系统的时间冗余性和结构相似性特征 ,提高系统模式匹配效率
正向推理(Forward-Chaining)和反向推理(Backward-Chaining)
(1)正向推理也叫演绎法,由事实驱动,从一个初始的事实出发,不断地从应用规则得出结论 。首先在候选队列中选择一条规则作为启用规则进行推理,记录其结论作为下一步推理的证据 。如此重复这个过程,直到再无可用规则可被选用或者求得了所要求的解为止 。
(2)反向推理也叫归纳法,由目标驱动,首先提出某个假设,然后寻找支持该假设的证据,若所需的证据都能找到,说明原假设是正确的,若无论如何都找不到所需要的证据,则说明原假设不成立,此时需要另作新的假设 。
2.3.2 rete算法Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年发表的论文中所阐述的算法 ,该算法提供了专家系统的一个高效实现 。自 Rete 算法提出以后 , 它就被用到一些大型的规则系统中 , 像 ILog、Jess、JBossRules 等都是基于 RETE 算法的规则引擎。
Rete 在拉丁语中译为”net”,即网络 。Rete 匹配算法是一种进行大量模式集合和大量对象集合间比较的高效方法,通过网络筛选的方法找出所有匹配各个模式的对象和规则 。
其核心思想是将分离的匹配项根据内容动态构造匹配树,以达到显著降低计算量的效果 。Rete 算法可以被分为两个部分:规则编译和规则执行。当 Rete 算法进行事实的断言时,包含三个阶段:匹配、选择和执行,称做 match-select-act cycle 。
2.4 规则引擎应用场景业务领域示例财务决策贷款发放,征信系统库存管理及时的供应链路票价计算航空,传播,火车及其他公共汽车运输生产采购系统产品原材料采购管理风控系统风控规则计算促销平台系统满减,打折,加价购2.5 Drools 介绍Drools 具有一个易于访问企业策略、易于调整以及易于管理的开源业务 规则引擎,符合业内标准,速度快、效率高 。业务分析师或审核人员可以利用它轻松查看业务规则,从而检验已编码的规则是否执行了所需的业务规则 。其前身是Codehaus 的一个开源项目叫 Drools,后被纳入 JBoss 门下,更名为 JBoss Rules,成为了 JBoss应用服务器的规则引擎 。
Drools 被分为两个主要的部分:编译和运行时 。编译是将规则描述文件按 ANTLR 3语法进行解析,对语法进行正确性的检查,然后产生一种中间结构“descr”,descr 用 AST 来描述规则 。目前,Drools支持四种规则描述文件,分别是:drl 文件、 xls 文件、brl 文件和 dsl 文件,其中,常用的描述文件是 drl 文件和 xls文件,而 xls 文件更易于维护,更直观,更为被业务人员所理解 。运行时是将 AST传到 PackageBuilder,由PackagBuilder来产生 RuleBase,它包含了一个或多个 Package 对象 。
3 .消费赠送积分案例
Drools 规则引擎应用 看这一篇就够了

文章插图
上图为实际用法:
3.1 第一步: 创建工程,引入jar由于当前java开发,普通使用springboot ,本课程以springboot为基本框架演示
jar 依赖,注意,排除spring相关依赖
<!-- 规则引擎 --><dependency><groupId>org.kie</groupId><artifactId>kie-spring</artifactId><version>${drools.version}</version><exclusions><exclusion><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-core</artifactId></exclusion><exclusion><groupId>org.springframework</groupId><artifactId>spring-context</artifactId></exclusion></exclusions></dependency>3.2 创建 drools 自动配置类drools 在spring 或者springboot中用法一样,其实就是创建好一些bean
package com.ityml.drools.config;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;/** * <p> 规则引擎自动配置类 </p> * @author ityml * @date 2019/9/10 11:20 */@Configurationpublic class DroolsAutoConfiguration {private static final String RULES_PATH = "rules/";private KieServices getKieServices() {return KieServices.Factory.get();}@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(() -> kieRepository.getDefaultReleaseId());KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());kieBuilder.buildAll();KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());return kieContainer;}@Bean@ConditionalOnMissingBean(KieBase.class)public KieBase kieBase() throws IOException {return kieContainer().getKieBase();}}3.2订单实体类@Data@Accessors(chain = true)public class Order {/*** 订单原价金额*/private int price;/***下单人*/private User user;/***积分*/private int score;/*** 下单日期*/private Date bookingDate;} 3.3规则引擎文件package rulesimport com.ityml.drools.entity.Orderrule "zero"no-loop truelock-on-active truesalience 1when$s : Order(amout <= 100)then$s.setScore(0);update($s);endrule "add100"no-loop truelock-on-active truesalience 1when$s : Order(amout > 100 && amout <= 500)then$s.setScore(100);update($s);endrule "add500"no-loop truelock-on-active truesalience 1when$s : Order(amout > 500 && amout <= 1000)then$s.setScore(500);update($s);endrule "add1000"no-loop truelock-on-active truesalience 1when$s : Order(amout > 1000)then$s.setScore(1000);update($s);end3.4客户端/** * 需求 * 计算额外积分金额 规则如下: 订单原价金额 * 100以下, 不加分 * 100-500 加100分 * 500-1000 加500分 * 1000 以上 加1000分 */public class DroolsOrderTests extends DroolsApplicationTests {@Resourceprivate KieContainer kieContainer;@Testpublic void Test() throws Exception {List<Order> orderList = getInitData();for (Order order : orderList) {if (order.getAmout() <= 100) {order.setScore(0);addScore(order);} else if (order.getAmout() > 100 && order.getAmout() <= 500) {order.setScore(100);addScore(order);} else if (order.getAmout() > 500 && order.getAmout() <= 1000) {order.setScore(500);addScore(order);} else {order.setScore(1000);addScore(order);}}}@Testpublic void droolsOrderTest() throws Exception {KieSession kieSession = kieContainer.newKieSession();List<Order> orderList = getInitData();for (Order order: orderList) {// 1-规则引擎处理逻辑kieSession.insert(order);kieSession.fireAllRules();// 2-执行完规则后, 执行相关的逻辑addScore(order);}kieSession.dispose();}private static void addScore(Order o){System.out.println("用户" + o.getUser().getName() + "享受额外增加积分: " + o.getScore());}private static List<Order> getInitData() throws Exception {List<Order> orderList = new ArrayList<>();DateFormat df = new SimpleDateFormat("yyyy-MM-dd");{Order order = new Order();order.setAmout(80);order.setBookingDate(df.parse("2015-07-01"));User user = new User();user.setLevel(1);user.setName("Name1");order.setUser(user);order.setScore(111);orderList.add(order);}{Order order = new Order();order.setAmout(200);order.setBookingDate(df.parse("2015-07-02"));User user = new User();user.setLevel(2);user.setName("Name2");order.setUser(user);orderList.add(order);}{Order order = new Order();order.setAmout(800);order.setBookingDate(df.parse("2015-07-03"));User user = new User();user.setLevel(3);user.setName("Name3");order.setUser(user);orderList.add(order);}{Order order = new Order();order.setAmout(1500);order.setBookingDate(df.parse("2015-07-04"));User user = new User();user.setLevel(4);user.setName("Name4");order.setUser(user);orderList.add(order);}return orderList;}}3.5 drools开发小结3.5.1 drools 组成drools规则引擎由以下几部分构成:
  • Working Memory(工作内存)
  • Rules(规则库)
  • Facts
  • Production memory
  • Working memory:
  • Agenda
如下图所示:
Drools 规则引擎应用 看这一篇就够了

文章插图
3.5.2 相关概念说明Working Memory:工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,所以我们开发的应用程序只需要将我们的数据插入到Working Memory中即可,例如本案例中我们调用kieSession.insert(order)就是将order对象插入到了工作内存中 。
Fact:事实,是指在drools 规则应用当中,将一个普通的JavaBean插入到Working Memory后的对象就是Fact对象,例如本案例中的Order对象就属于Fact对象 。Fact对象是我们的应用和规则引擎进行数据交互的桥梁或通道 。
Rules:规则库,我们在规则文件中定义的规则都会被加载到规则库中 。
Pattern Matcher:匹配器,将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,匹配成功的规则将被激活并放入Agenda中 。
Agenda:议程,用于存放通过匹配器进行模式匹配后被激活的规则 。
3.5.3 规则引擎执行过程3.5.4 KIE介绍在上述分析积分兑换的过程中,简单地使用了 "kie "开头的一些类名,Kie全称为Knowledge Is Everything,即"知识就是一切"的缩写,是Jboss一系列项目的总称 。官网描述:这个名字渗透在GitHub账户和Maven POMs中 。随着范围的扩大和新项目的展开,KIE(Knowledge Is Everything的缩写)被选为新的组名 。KIE的名字也被用于系统的共享方面;如统一的构建、部署和使用 。
4.规则文件开发4.1 规则文件构成在使用Drools时非常重要的一个工作就是编写规则文件,通常规则文件的后缀为.drl 。
drl是Drools Rule Language的缩写 。在规则文件中编写具体的规则内容 。
一套完整的规则文件内容构成如下:
关键字描述package包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用import用于导入类或者静态方法global全局变量function自定义函数query查询rule end规则体Drools支持的规则文件,除了drl形式,还有Excel文件类型的 。
4.2 规则体语法结构规则体是规则文件内容中的重要组成部分,是进行业务规则判断、处理业务结果的部分 。
规则体语法结构如下:
rule "ruleName"attributeswhenLHSthenRHSendrule:关键字,表示规则开始,参数为规则的唯一名称 。
attributes:规则属性,是rule与when之间的参数,为可选项 。
when:关键字,后面跟规则的条件部分 。
LHS(Left Hand Side):是规则的条件部分的通用名称 。它由零个或多个条件元素组成 。如果LHS为空,则它将被视为始终为true的条件元素 。(左手边)
then:关键字,后面跟规则的结果部分 。
RHS(Right Hand Side):是规则的后果或行动部分的通用名称 。(右手边)
end:关键字,表示一个规则结束 。
4.3 注释在drl形式的规则文件中使用注释和Java类中使用注释一致,分为单行注释和多行注释 。
单行注释用"//"进行标记,多行注释以"/