一、简介

  • 项目官网:Drools - Drools - Business Rules Management System (Java™, Open Source)
    源码:GitHub - kiegroup/drools: Drools is a rule engine, DMN engine and complex event processing (CEP) engine for Java.
  • Drools是一个绝对重量级的规则引擎,很多像金融行业、电信行业的大公司都在使用它作为规则引擎。
  • Drools通过 事实、规则和模式相互组合来完成工作,drools在开源规则引擎中使用率最广,但是在国内企业使用偏少,保险、支付行业使用稍多。
  • Drools 是用 Java 语言编写的开放源码规则引擎,使用 Rete 算法对所编写的规则求值。Drools 允许使用声明方式表达业务逻辑。可以使用非 XML 的本地语言编写规则,从而便于学习和理解。并且,还可以将 Java 代码直接嵌入到规则文件中,这令 Drools 的学习更加吸引人。

二、优缺点分析

优点

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

缺点

  • 业务分析师无法独立完成规则配置:由于规则主体DSL是编程语言(支持Java, Groovy, Python),因此仍然需要开发工程师维护
  • 规则规模变大以后也会变得不好维护,相对硬编码的优势便不复存在
  • 规则的语法仅适合扁平的规则,对于嵌套条件语义(then里嵌套when…then子句)的规则只能将条件进行笛卡尔积组合以后进行配置,不利于维护。

三、Drools相关概念:

  • 事实(Fact):对象之间及对象属性之间的关系
  • 规则(rule):是由条件和结论构成的推理语句,一般表示为if…Then。一个规则的if部分称为LHS,then部分称为RHS。
  • 模式(module):就是指IF语句的条件。这里IF条件可能是有几个更小的条件组成的大条件。模式就是指的不能在继续分割下去的最小的原子条件。

四、Drools的基本语法

一个规则可以包含三个部分:

  • 属性部分:定义当前规则执行的一些属性等,比如是否可被重复执行、过期时间、生效时间等。
  • 条件部分,即LHS,定义当前规则的条件,如 when Message(); 判断当前workingMemory中是否存在Message对象。
  • 结果部分,即RHS,这里可以写普通java代码,即当前规则条件满足后执行的操作,可以直接调用Fact对象的方法来操作应用。

4.1 规则示例:

rule "name"no-loop truelock-on-active truewhen$message:Message(status == 0)thenSystem.out.println("fit");$message.setStatus(1);update($message);
end
  • package: 与Java语言类似,drl的头部需要有package和import的声明,package不必和物理路径一致,这里只是一个逻辑区分。
  • import: 导入java Bean的完整路径,也可以将Java静态方法导入调用。
  • rule: 规则名称,需要保持唯一 。
  • no-loop: 定义当前的规则是否允许多次循环执行,默认是 false允许循环执行,也就是当前的规则只要满足条件,可以无限次执行。在对当前传入workingMemory中的Fact对象进行修改或者个数的增减,比如update方法,这种操作会触发规则的重新匹配执行。如果是true,则规则只执行一次,如果本身的RHS部分有update等触发规则重新执行的操作,也不会再次执行当前规则。
  • lock-on-active: 将lock-on-active属性的值设置为true,可避免因某些Fact对象被修改而使已经执行过的规则再次被激活执行。因为一个规则的重复执行不一定是本身触发的,也可能是其他规则触发的,所以这个是no-loop的加强版。
  • date-expires:设置规则的过期时间,默认的时间格式:“日-月-年”,中英文格式相同,但是写法要用各自对应的语言,比如中文:"29-七月-2010",但是还是推荐使用更为精确和习惯的格式,这需要手动在java代码中设置当前系统的时间格式,后续提及。
  • date-effective:设置规则的生效时间,时间格式同上。
  • duration:规则定时,duration 3000 ,3秒后执行规则
  • salience: 用来设置规则执行的优先级,salience 属性的值是一个数字,数字越大执行优先级越高, 同时它的值可以是一个负数。默认情况下,规则的 salience 默认值为 0。如果不设置规则的 salience 属性,那么执行顺序是随机的。
  • when: 条件语句,就是当到达什么条件的时候执行
  • then: 根据条件的结果,来执行什么动作
  • end: 规则结束

4.2 规则的条件部分,即LHS部分

wheneval(true)$customer:Customer()$message:Message(status==0)

上述罗列了三个条件,当前规则只有在这三个条件都匹配的时候才会执行RHS部分。

  • eval(true):是一个默认的api,true 无条件执行,类似于 while(true)
  • $customer:Customer():表示当前的workingMemory存在Customer类
  • $message:Message(status==0) 这句话标示的:当前的workingMemory存在Message类型并且status属性的值为0的Fact对象,这个对象通常是通过外部java代码插入或者自己在前面已经执行的规则的RHS部分中insert进去的。

4.3 Drools中条件操作符

Drools提供了十二中类型比较操作符:< 、<=、>、>=、==、!=、contains、not contains、memberOf、not memberOf、matches、not matches,并且这些条件都可以组合使用。

  $order:Order(name=="qu")$message:Message((status==0 ||  (status > 1 && status <=100)) && orders contains $order && $order.name=="qu")
  • 条件组合:Message(status==0 || (status > 1 && status <=100))
  • Fact对象private属性的操作:RHS中对Fact对象private属性的操作必须使用getter和setter方法,而RHS中则必须要直接用.的方法去使用,比如:$order.name=="qu"
  • contains:对比是否包含操作,操作的被包含目标可以是一个复杂对象也可以是一个简单的值。 $message:Message(status==0 && names contains "网易" && names.size >= 1)。上述的条件中,status必须是0,并且names列表中含有“网易”并且列表长度大于等于1(names是一个List)。
  • matches:正则表达式匹配,与java不同的是,不用考虑'/'的转义问题
  • memberOf:判断某个Fact属性值是否在某个集合中,与contains不同的是他被比较的对象是一个集合,而contains被比较的对象是单个值或者对象。

注意:如果条件全部是 &&关系,可以使用“,”来替代,但是两者不能混用

4.4 规则的结果部分

当规则条件满足,则进入规则结果部分执行,结果部分可以是纯java代码,比如:

thenSystem.out.println("OK"); //会在控制台打印出ok
end
  • insert:往当前workingMemory中插入一个新的Fact对象,会触发规则的再次执行,除非使用no-loop限定;
  • update:更新
  • modify:修改,与update语法不同,结果都是更新操作
  • retract:删除
  • function:定义一个方法,如:
function void console {System.out.println();StringUtils.getId();// 调用外部静态方法,StringUtils必须使用import导入,getId()必须是静态方法
}
  • declare:可以在规则文件中定义一个class,使用起来跟普通java对象相似,你可以在RHS部分中new一个并且使用getter和setter方法去操作其属性。
declare Address@author(quzishen) // 元数据,仅用于描述信息@createTime(2011-1-24)city : String @maxLengh(100)postno : int
end

'@'是元数据定义,用于描述数据的数据~,没什么执行含义。
你可以在RHS部分中使用Address address = new Address()的方法来定义一个对象。

更多的规则语法,可以参考其他互联网资料,推荐:
http://wenku.baidu.com/view/a6516373f242336c1eb95e7c.html

4.5 pom中加入依赖

<!-- Drools规则引擎包 start -->
<dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>6.5.0.Final</version>
</dependency>
<dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>6.5.0.Final</version>
</dependency>
<!-- Drools规则引擎包 end -->

五、案例

下面是一个增加积分的列子,主要实现用户购买的金额达到一定量后送积分,具体的规则如下:

100元以下, 不加分
100元-500元 加100分
500元-1000元 加500分
1000元 以上 加1000分

有两种实现方案,一种是规则直接写在文件中,一种是规则写在数据库中。

5.1 规则写在文件中

Point.drl 文件

在src/main/resources目录下新建rules.point文件夹,新建Point.drl文件

//这里的package属性是一个逻辑区分,不需要与这个文件路径相对应
package point.rulesimport com.xiaolyuh.domain.model.Orderrule "zero"no-loop truelock-on-active truesalience 1when$s : Order(amout <= 100)then$s.setScore(0);System.out.println("不加积分");update($s);
endrule "add100"no-loop truelock-on-active truesalience 1when$s : Order(amout > 100 && amout <= 500)then$s.setScore(100);System.out.println("加100积分");update($s);
endrule "add500"no-loop truelock-on-active truesalience 1when$s : Order(amout > 500 && amout <= 1000)then$s.setScore(500);System.out.println("加500积分");update($s);
endrule "add1000"no-loop truelock-on-active truesalience 1when$s : Order(amout > 1000)then$s.setScore(1000);System.out.println("加500积分");update($s);
end

5.2 kmodule.xml

这里需要有一个配置文件告诉代码规则文件drl在哪里,在drools中这个文件就是kmodule.xml,放置到resources/META-INF目录下,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule"><!-- 这里的packages属性就是规则文件的文件路径 --><kbase name="point_rule" packages="rules.point"><ksession name="point_ksession"/></kbase></kmodule>

以下对配置说明进行简单说明:

  • Kmodule: 中可以包含一个到多个 kbase,分别对应 drl 的规则文件。
  • Kbase 需要一个唯一的 name,可以取任意字符串。
  • packages: 为drl文件所在resource目录下的路径。注意区分drl文件中的package与此处的package不一定相同。多个包用逗号分隔。默认情况下会扫描 resources目录下所有(包含子目录)规则文件。
  • kbase的default属性:标示当前KieBase是不是默认的,如果是默认的则不用名称 就可以查找到该 KieBase,但每个 module 最多只能有一个默认 -
    KieBase。
  • kbase的ksession: kbase下面可以有一个或多个 ksession,ksession 的 name 属性必须设置,且必须唯一。

5.3 DroolsUtil

通过该类加载kmodule.xml文件,并获得KieSession。

/*** @author yuhao.wang*/
public class DroolsUtil {public static final Logger log = LoggerFactory.getLogger(DroolsUtil.class);/*** 线程安全单例*/private static volatile KieServices kieServices = KieServices.Factory.get();/*** KieBase容器,线程安全单例*/private static volatile KieContainer kieContainer;/*** 加载KieContainer容器*/public static KieContainer loadKieContainer() throws RuntimeException {//通过kmodule.xml 找到规则文件,这个文件默认放在resources/META-INF文件夹log.info("准备创建 KieContainer");if (kieContainer == null) {log.info("首次创建:KieContainer");// 设置drools的日期格式System.setProperty("drools.dateformat", "yyyy-MM-dd HH:mm:ss");//线程安全synchronized (DroolsUtil.class) {if (kieContainer == null) {// 创建ContainerkieContainer = kieServices.getKieClasspathContainer();// 检查规则文件是否有错Results results = kieContainer.verify();if (results.hasMessages(Message.Level.ERROR)) {StringBuffer sb = new StringBuffer();for (Message mes : results.getMessages()) {sb.append("解析错误的规则:").append(mes.getPath()).append(" 错误位置:").append(mes.getLine()).append(";");}throw new RuntimeException(sb.toString());}}}}log.info("KieContainer创建完毕");return kieContainer;}/*** 根据kiesession 名称创建KieSession ,每次调用都是一个新的KieSession* @param name kiesession的名称* @return 一个新的KieSession,每次使用后要销毁*/public static KieSession getKieSessionByName(String name) {if (kieContainer == null) {kieContainer = loadKieContainer();}KieSession kieSession;try {kieSession = kieContainer.newKieSession(name);} catch (Exception e) {log.error("根据名称:" + name + " 创建kiesession异常");throw new RuntimeException(e);}return kieSession;}}

5.4 执行规则

DroolsScoreExampleTest.java

/*** 计算额外积分金额 规则如下: 订单原价金额* 100以下, 不加分* 100-500 加100分* 500-1000 加500分* 1000 以上 加1000分** @param args* @throws Exception*/public static final void main(final String[] args) throws Exception {// 通过工具类去获取KieSessionKieSession ksession = DroolsUtil.getKieSessionByName("point_ksession");List<Order> orderList = getInitData();try {for (int i = 0; i < orderList.size(); i++) {Order o = orderList.get(i);ksession.insert(o);ksession.fireAllRules();// 执行完规则后, 执行相关的逻辑addScore(o);}} catch (Exception e) {} finally {ksession.destroy();}}

5.5 基于数据库的方式

表结构

CREATE TABLE `rule` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`rule_key` varchar(255) NOT NULL DEFAULT '' COMMENT '规则编码',`version` varchar(255) NOT NULL DEFAULT '' COMMENT '规则编码',`content` varchar(2048) NOT NULL DEFAULT '' COMMENT '规则n内容',`create_time` datetime NOT NULL,`update_time` datetime DEFAULT NULL,PRIMARY KEY (`id`),UNIQUE KEY `uk_rule_key` (`rule_key`) USING BTREE,KEY `uk_update_time` (`update_time`) USING BTREE,KEY `uk_version` (`version`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

5.6 获取KieSession

RuleServiceImpl.java

@Override
public KieSession getKieSessionByName(String ruleKey) {StatefulKnowledgeSession kSession = null;try {long startTime = System.currentTimeMillis();// TODO 可以缓存到本地,不用每次都去查数据库Rule rule = ruleMapper.findByRuleKey(ruleKey);KnowledgeBuilder kb = KnowledgeBuilderFactory.newKnowledgeBuilder();// 装入规则,可以装入多个kb.add(ResourceFactory.newByteArrayResource(rule.getContent().getBytes("utf-8")), ResourceType.DRL);// 检查错误KnowledgeBuilderErrors errors = kb.getErrors();for (KnowledgeBuilderError error : errors) {System.out.println(error);}// TODO 没有找到替代方法KnowledgeBase kBase = KnowledgeBaseFactory.newKnowledgeBase();kBase.addKnowledgePackages(kb.getKnowledgePackages());// 创建kSessionkSession = kBase.newStatefulKnowledgeSession();long endTime = System.currentTimeMillis();logger.info("获取kSession耗时:{}", endTime - startTime);} catch (UnsupportedEncodingException e) {e.printStackTrace();}return kSession;
}

5.7 执行规则

RuleController.java

@ResponseBody
@RequestMapping("address")
public Object test(int num) {AddressCheckResult result = new AddressCheckResult();Address address = new Address();address.setPostcode(generateRandom(num));String ruleKey = "score";KieSession kieSession = ruleService.getKieSessionByName(ruleKey);int ruleFiredCount = 0;try {kieSession.insert(address);kieSession.insert(result);ruleFiredCount = kieSession.fireAllRules();} catch (Exception e) {logger.warn(e.getMessage(), e);} finally {if (kieSession != null) {kieSession.destroy();}}System.out.println("触发了" + ruleFiredCount + "条规则");if (result.isPostCodeResult()) {System.out.println("规则校验通过");}return "ok";
}

参考:

  • https://zhuanlan.zhihu.com/p/28528925
  • http://blog.csdn.net/quzishen/article/details/6163012

源码地址:
spring-boot-student-drools 工程

规则引擎JBoss Drools简介相关推荐

  1. 规则引擎之Drools

    规则引擎之Drools Drools概述 规则引擎构成 规则文件构成 规则体语法结构 规则属性 比较操作符 Pattern模式匹配 执行指定规则 Drools内置方法 Drools的基本使用 添加依赖 ...

  2. jboss规则引擎KIE Drools 6.3.0 Final 教程(1)

    前言 目前世面上中文的KIE DROOLS Workbench(JBOSS BRMS)的教程几乎没有,有的也只有灵灵碎碎的使用机器来翻译的(翻的不知所云)或者是基于老版本的JBOSS Guvnor即5 ...

  3. jboss规则引擎KIE Drools 6.3.0 Final 教程(2)

    使用JAVA程序调用规则-运行KIE-DROOLS上的规则 第一步:建立一个MAVEN的Java工程 POM.XML 给出pom.xml文件 <project xmlns="http: ...

  4. 常见的规则引擎(Drools,RuleBook,Easy Rules等)对比

    参考文章: https://www.jianshu.com/p/96cd60059aae 规则引擎调研 - 人在江湖之诗和远方 - 博客园 java开源规则引擎比较_常用规则引擎比较分析_学校砍了我的 ...

  5. 【规则引擎】一、规则引擎简介

    (第一章规则引擎学习入门之规则引擎简介)# 系列文章目录 规则引擎简介 前言 一.为什么要使用规则引擎? 1.不使用规则引擎的规则执行现状 2. 规则引擎优点 二.规则引擎的功能 三.规则引擎的分类实 ...

  6. 规则引擎和流程引擎我该怎么理解

    流程引擎 什么是流程引擎 流程引擎就是"业务过程的部分或整体在计算机应用环境下的自动化",它主要解决的是"使在多个参与者之间按照某种预定义的规则传递文档.信息或任务的过程 ...

  7. 《Drools7.0.0.Final规则引擎教程》第1章 Drools简介

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

  8. 规则引擎drools系列(一)

    规则引擎 Drools 1. 问题引出 现有一个在线申请信用卡的业务场景,用户需要录入个人信息,如下图所示: //此处为伪代码 ​ //检查用户信息合法性,返回true表示检查通过,返回false表示 ...

  9. 规则引擎 Drools

    规则引擎 Drools 文章目录 规则引擎 Drools 1. 问题引出 2. 规则引擎概述 2.1 什么是规则引擎 2.2 使用规则引擎的优势 2.3 规则引擎应用场景 2.4 Drools介绍 3 ...

最新文章

  1. linux中更新python_linux下面升级 Python版本并修改yum属性信息
  2. 局域网屏幕共享_ShareMouse for Mac(鼠标键盘共享)
  3. 进程间通信(IPC)介绍(转)
  4. cisco routemap 能在出接口调用吗_潍坊驰燃一号燃料能不能合法在家经营,手续好办吗?...
  5. POJ-3635 Full Tank? 变形最短路
  6. 并行编译 Xoreax IncrediBuild
  7. 安装redis k8s_K8S 生态周报| Docker v19.03.6-rc2 发布
  8. [转]给新同学的礼仪建议
  9. javascript 轮播图(缓存效果)详细篇
  10. 需求文档2_The Battle of Polytopia
  11. 银行业应用系统监控的维度与目标
  12. 关于python中的复数的虚部可以j或i_Python 中的复数问题
  13. 小程序-蓝牙打印写数据部分CPCL指令方式,解决偶尔打印打印机指令情况(传输的数据被风吹跑了~)
  14. 网店营销成为运营商营销的重要平台
  15. python 删除所有空间说说_怎么批量删除QQ空间说说?
  16. 微信服务器维护中什么意思,公众号运营维护是什么意思,微信公众号的日常运营与维护技巧!...
  17. 面试中,被问到“哑口无言”的瞬间怎么办?
  18. 如何下载VS2005程序到开发板上(总结)
  19. matlab hdf5,无法使用最新的HDF5打开matlab文件
  20. ABP实践(5)-abp前端vue框架之IView实现三级菜单(博友需要特此分享)

热门文章

  1. 从今天开始每天坚持认真学习编程
  2. Oracle 11g数据库下载安装教程
  3. java面试题13--获取任意一年的二月有多少天
  4. 中国汽车电子市场发展现状分析与展望
  5. C++基础:运算符优先级
  6. vue中选中行数据传递到下个页面
  7. 多测师肖sir_高级金牌讲师_性能测试之nmon(007)
  8. HDU2255 奔小康赚大钱
  9. Sui 从基础到编码实战
  10. 十六进制表示_计算机为什么用二进制和十六进制