Drool学习记录(一) 概念、Helloworld

article/2025/9/8 21:34:27

1 关于规则引擎

基于知识库和规则的专家系统是早期最主流的人工智能,不同于现在流行的基于统计、机器学习的智能算法,基于规则的算法相对来说更加直观和易于理解,毕竟如果简单理解的话,就是定义好了If-Than结构,从而让不同的输入得到相应的输出。但从现实角度出发,开发基于规则的业务系统很多时候是必须的,因为很多业务场景就是规定好的处理逻辑,比如一款游戏里,使用某个道具后给角色添加哪种效果,就是要用预先定义好的If-Than逻辑。
正如所有软件工程或设计模式教程里提到的,随着业务逻辑的扩张和更新,通过代码定义判断逻辑将变的越来越复杂并难以维护,因此规则引擎应运而生,其核心思想就是把规则(rule)和数据(fact)分离,我们自身的系统只维护数据的域(Domain),业务逻辑的判断、验证和运行交给规则引擎处理,当然我们还是要编写和维护规则文件的,不过因为规则已经独立于系统,这个工作就可以交给非程序开发者了,并且是在线和动态的。
在游戏应用、金融、航班系统等领域,软件系统面对的主要问题之一就是复杂多变业务规则和庞大并具有实时性的输入数据,因此这些领域规则引擎的应用也更加普遍。

2 关于Drools

2.1 简介

Drools引擎可以说是目前最有名的一个开源规则引擎,按照官网的解释,整个Drools是一个Business Rules Management System (BRMS),Drools引擎是其中的核心组件,基于原生JAVA编写,遵循Apache 协议并且加入Maven中心仓储。
另一方面,Drools又隶属于KIE (Knowledge Is Everything)这个大项目,按照官方教程的说明,KIE包括了多个相互依赖的子项目用以提供业务自动化管理的解决方案,例如OptaPlanner就是把Drools作为了重要的一个子组件(OptaPlanner也是我后面计划记录的)。

2.2 Drools的组件和结构

首先要了解构成Drools引擎处理模式的几个基本成分:

  • Rule:业务规则,一个rule必须有一个触发它的condition,以及触发后对应的action,其实就是If-Than的意思
  • Fact:就是输入到Drools引擎进行rule匹配的数据,fact不仅仅只是输入引擎的原始数据,也包括某些rule触发后生成的中间fact
  • Production memory:Drools引擎内部会将所有的rule信息放在这个空间内
  • Working memory: Drools引擎内部会将所有的fact放在这个空间内
  • Agenda:在Drools引擎进行执行操作前,会将激活的rule在这个空间进行注册和存储, 正如该单词的字面意思,引擎会把激活的rule按照优先级生成运行序列表,按照顺序依次执行rule

image.png
对照上面这张图,Drools引擎大致是这样工作的:引擎启动后,首先进行编译工作,就是把我们定义好的rule加载到Production memory中,我们也可以把这个过程称为"构建知识库";当我们的业务系统生成新的数据并传入到引擎,引擎把这些数据作为fact存储进Working memory;接着引擎开始"模式匹配(pattern matching)"工作,即对Working memory中的fact,匹配rule的condition项,如果匹配了,那么把这个rule加入到Agenda里面;最后是真正的执行操作,引擎会对Agenda里的rule按照优先级进行序列安排,然后执行rule定义好的action操作

2.3 Rete算法解释

2.3.1 关于Rete算法

虽然上面关于Drools引擎处理过程的分析并不是十分繁琐,但是对于初次接触的人来说还是很困惑的,我在看教程的时候就很疑惑,为什么处理If-than逻辑需要处理地这么复杂,尤其是Agenda这个组件,很难理解它的意义。通过查阅官方文档里对算法部分的介绍,其实Drools引擎之所以这样处理,主要是由它的算法决定的。Drools引擎核心的规则算法叫Phreak,这是以Rete算法为基础扩展出的算法。事实上,目前几乎所有规则引擎的核心算法都是以Rete算法及其衍生算法为基础的。因此,如果我们想对Drools理解和使用得更自如,还是有必要先去了解一下Rete算法的,当然没有必要研究地太透彻,主要目的是要了解这个算法的目的、意义和大致流程。通过了解Rete算法我们也可以认识到,基于规则的专家系统并没有想象的简单,它也需要严谨和复杂的数学理论做支持的。

下面这段关于Rete算法的通俗解释我直接参考自这篇文章How the Rete Algorithm Works,该作者就认识Rete算法的发明者,整片文章可谓酣畅淋漓。

我们以一个常旅客计划(航空里程计算)作为例子。航空公司对于乘坐频繁的商务人士有奖励里程服务,具体的奖励里程计算需要遵循非常多的规则,例如下面列出的一些:

  • If 去年或今年的奖励里程数 >25,000,than 用户账户评定为白银
  • if 去年或今年的奖励里程数 >100,000,than 用户账户评定为黄金
  • if 航班里程<500英里,than 奖励500
  • if 航班里程>=500, than 奖励实际的航班里程数
  • if 如果是商务舱, than 额外奖励本次航班50%的里程
  • if 该用户是黄金并且该航班不属于合作公司, than 额外奖励本次航班100%的里程
  • if 该用户是白银并且该航班不属于合作公司, than 额外奖励本次航班20%的里程
  • if 该用户加入了本月3次航班奖励5000英里计划,并且本月达到了3次航班, than 奖励5000英里

我们可以想象一下,如果我们简单地通过手写代码实现上述业务逻辑,会遇到一个很显著的问题:如何安排这些rule的执行次序? 因为实际上很多rule之间是有关联性的, 比如某次航班后用户奖励了里程后达到了黄金评级,那么相应地又要触发对黄金用户的奖励措施,这样庞大又有关联性的rule库使得手写业务逻辑很难保证和维护正确性和完备性。除此之外,里程计算系统实际处理的不是只有这一个用户,往往是需要同时处理大批量的用户账户的,这使得手写业务逻辑也需要面临性能问题。

2.3.2 Rete算法过程描述

我们用Rete算法的思想来处理这个问题(当然只是说明算法的思想,其具体过程不会很严谨)。首先要做的工作是根据rule库,构建Rete网络。Rete网络Rete算法的核心,它有多个结点构成,每个结点关联了一系列满足相应condition的对象。我们先构造由两个Alpha结点构成的判别树,每个Alpha结点对应了一种类型(域),在这个例子里,有两个对象类型Account(用户账户)和Flight(航班)。
Drools_sample_1.png
各个rule所定义的condition也作为结点加入到对应的Alpha结点或父结点下,从而扩充判决树。如下所示:

  • Account:

    • 评级是白银
    • 评级是黄金
    • 去年或今年的奖励里程数>25k
    • 去年或今年的奖励里程数>100k
    • 加入了本月3次航班奖励5000英里计划
      • 本月达到了3次航班
  • Flight

    • 里程数>=500
    • 里程数<500
    • 所属公司不是合作公司
    • 所属公司是合作公司
    • 是商务舱

Drools_sample_1(1).png
最后我们需要利用condition分支生成action,这里可能就需要不同的分支进行链接。
Drools_sample_1(2).png
这样我们就构建好Rete网络了,如果你有新的rule加入,只需要在原网络基础上增加结点。

接着就对输入的fact进行评价(Evaluate)。Evaluate过程就是把输入的数据在Rete网络里过一遍,从而识别出那些满足condition的rule(也就是激活过程)。我们输入这样一个fact: Joe乘坐非合作公司的经济舱航班从A飞到B,这趟航班有2,419英里,并且他的账户上已经有150k的奖励里程。我们将这个fact输入到Rete网络的根节点,在Account的Alpha结点对应的分支下,因为Joe账户上150k的奖励里程,满足了">100k"的condition,所以"if 去年或今年的奖励里程数 >100,000,than 用户账户评定为黄金"这条rule被激活。在这个例子我们还假设这个规则系统不存储用户评级系统状态并且需要每次运行时重新计算,这样我们就不去激活和用户评级相关的rule。同样的,在Flight这个分支内,激活了"if 航班里程>=500, than 奖励实际的航班里程数"这条rule。"not partner"这个condition虽然满足,但是因为用户评级目前属于未知因此图上有链接的rule尚未激活,如下图所示,激活的路径用a标注。
Drools_sample_1(5).png
现在我们可以认为存放激活rule的agenda内有两个rule,真实情况下,agenda内的rule的执行顺序需要根据它们的优先级来判定,这个例子里我们假设右边的这个rule(航班里程>=500)先执行,即奖励了2419的里程。
第一条rule执行后,fact发生了改变,这个变化需要进行传播,也就是再次过一下Rete网络来激活某些rule。原fact的Flight部分没有发生变化,其分支不需要进行重新
Evaluate; Account分支则需要重新Evaluate,当然这个例子里这次重新Evaluate没有引起变化。经过这次fact传播,agenda内仍然剩下一个"if 去年或今年的奖励里程数 >100,000,than 用户账户评定为黄金"的rule,那么继续执行这个rule的action,即将用户评级设为黄金。Account信息再次更新,这次传播将导致"if 该用户是黄金并且该航班不属于合作公司, than 额外奖励本次航班100%的里程"的rule生效,因此最终Joe的奖励里程为4839。如下图所示,激活的路径用b标注。
Drools_sample_1(6).png
上面就是Rete算法的一个通俗解释。Rete算法充分运用了推理技术,达到了大规模业务规则下构建评估(Evaluate)和保证状态序列正确性(ordering the statements properly)的快速高效。算法内的这些构建网络、Evaluate、Agenda等过程也正是上文提到的Drools引擎组件和工作模式的由来。

3 写一个Drools的hello world

最后我们写一个应用Drools引擎的helloworld程序来熟悉其开发。我们就假设这样一个业务场景:一个ID证件颁发部门要对公民进行证件颁发,如果这个人的年龄在18岁以下,颁发Child证件,如果他的年龄大于等于18岁,则颁发Adult证件。

3.1 新建工程

打开Intellij Idea, 新建一个Maven工程,选择quickstart的archetype,等待项目完全生成。
1.PNG
在pom.xml里添加drools的依赖,等待相关jar包获取完成。

  <dependencies>...other dependencies<dependency><groupId>org.kie</groupId><artifactId>kie-api</artifactId><version>7.32.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>7.32.0.Final</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>7.32.0.Final</version></dependency><dependency><groupId>org.kie</groupId><artifactId>kie-ci</artifactId><version>7.32.0.Final</version></dependency></dependencies>

添加resources文件夹, 里面再新建META-INF和rules两个子文件,META-INF是Drools引擎规定的默认结构,存放Drools知识库和会话配置;rules文件夹存放规则文件,这个不是必须的,你可以起个其他的名字,或者干脆不需要这个子文件夹,直接把规则文件放resources下。
2.PNG

3.2 创建域

接着我们定义这个场景需要用到的class。这个业务场景只需要定义一个Person类,有name和age属性
3.PNG

package drools.samples.Domain;public class Person {private String name;private Integer age;public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}
}

3.3 写一个规则文件

在rules子文件下新建一个sample1.drl文件,在里面定义Drools规则。

package drools.samples.rules.sample1import drools.samples.Domain.Personrule "Give Child ID card"
when
person: Person(age<18)
then
System.out.println("Give "+person.getName() + " a Child ID card");
endrule "Give Adult ID card"
when
person: Person(age>=18)
then
System.out.println("Give "+person.getName() + " a Adult ID card");
end

简单描述下:首先需要定义这个规则所属的package,这里我们把它归在"drools.samples.rules.sample1"; 然后用import局导入我们定义的Person类,这样规则引擎才能获取域信息;接着定义了两个rule,每个rule里的when子句就是condition, 我们在里面用person:Person(age<18)这种语法来对fact进行匹配,而then子句的内容就是condition对应的action,这里就简单的打印对应的通知信息。

3.4 配置文件

我们还需要在META-INF下添加一个名为kmodule.xml的配置文件,这是必须的步骤,不然Drools引擎无法知道怎么编译rule库。
4.PNG
kbase结点用于配置规则库或知识库的信息,这里我们指定了一个指向drools.samples.rules.sample1的名为rules1的kbase结点,意思就是这个rules1结点将使用我们在sample1.drl里定义好的规则(sample1.drl里定义的package名就是drools.samples.rules.sample1)来构建知识库;kbase结点下又配置了一个名为sample1的ksession结点,ksession代表了运行时的执行会话,可以在一个kbase下指定多个不同参数的session

3.5 主程序

最后我们写主程序:

public static void main( String[] args ){KieServices ks=KieServices.Factory.get();KieContainer kieContainer=ks.getKieClasspathContainer();KieSession kieSession=kieContainer.newKieSession("sample1");//就是kmodule.xml里定义的那个ksessionPerson person=new Person();person.setName("Joe");person.setAge(17);kieSession.insert(person);//把这个fact传入kieSession.fireAllRules();//开始规则检验}

执行结果
5.PNG


http://chatgpt.dhexx.cn/article/GDq1QyaS.shtml

相关文章

LiteFlow vs Drool的规则引擎深度对比

规则引擎的定义 两款框架的异同点 规则表达式 和Java的数据交换 API以及集成 侵入性耦合比较 规则的学习成本 是否有语言插件 规则的存储 规则的变更能否实时改变逻辑 是否有界面形态来支持 框架的性能表现 结语 Drools是一款老牌的java规则引擎框架&#xff0c;早…

Drools概述和基本原理

目录 ​编辑 一、Drools是什么&#xff1f; 二、Drools使用场景 三、Drool架构内容 3.1 总体架构 3.2 构成内容说明 3.2.1 Rules 3.2.2 Production memory 3.2.3 Facts 3.2.4 Working memory 3.2.5 Pattern matcher 3.2.6 Agenda 四、为什么要用规则引擎&#xff1f; 4.1 声明…

Uipath Workbook与Excel差别

学习目标&#xff1a; 了解Uipath Workbook与Excel 两组差别 Workbook 使用Workbook: 1.所有工作簿活动都将在后台执行。 2.不需要安装Microsoft Excel&#xff0c;只需在Excel应用程序中不打开文件&#xff0c;就可以更快、更可靠地进行某些操作。 3.仅适用于.xls和.xlsx文件…

Workbook.SaveAs方法

Workbook.SaveAs(FileName, FileFormat, Password, WriteResPassword, ReadOnlyRecommended, CreateBackup, AccessMode, ConflictResolution, AddToMru, TextCodepage, TextVisualLayout, Local) excel vba 保存代码 Sub 保存工作簿() ThisWorkbook.Save .save相当于按…

Workbook

工作需要。话不多说。 我们来看传统的poi。使用HSSFWorkbook创建2003格式为xls后缀的文档。如下&#xff1a; [html] view plain copy public String toTotal() { String strs "40288b163a72fd15013a730d44430004,40288b163a72fd15013a730d44430004," &q…

excel workbook

引用不同的workbook 1.索引号 workbooks.item&#xff08;3&#xff09;workbooks&#xff08;3&#xff09; 2.名称 当新建的文件&#xff0c;并没有保存的时候 workbooks(“book1”) 当已经存在的文件&#xff0c;要加上后缀 workbooks(“book1.xls”) workbook信息 cell…

UserWarning: Workbook contains no default style, apply openpyxl‘s default warn no default style 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。 本文主要介绍了UserWarning: W…

VBA基础,工作簿workbook相关的方法和属性

0 我觉得VBA和VB,VBS最大的区别就在A&#xff08;application&#xff09;上了 所以application里的 workbook , worksheet range ,cell等应该是VBA语言的核心&#xff0c;最复杂内容了 本身VBA里的VB语言部分&#xff0c;其实和很多语言语法结构也差不多每种语言的差别一在…

Python操作excel文件创建workbook和批量创建Sheet

第一步&#xff0c;创建workbook和sheet from openpyxl import WorkbookwbWorkbook() wb.create_sheet(index1,title"sheet2")wb.save(test1.xlsx)创建成功&#xff0c;workbook 名称test1&#xff0c;有sheet 和sheet2两张表 第二步&#xff0c;批量创建多个sheet …

SpringBoot使用Workbook读取excel中内容

SpringBoot使用Workbook读取excel中内容 maven文件中导入依赖 获取excel文件 FileInputStream fileInputStream new FileInputStream("文件路径");获取Workbook对象 Workbook workbook new XSSFWorkbook(fileInputStream);获取Sheet对象 Sheet sheet workbook.…

已解决ModuleNotFoundError: No module named ‘Workbook‘

已解决&#xff08;python xlwt库写入Excel表格报错&#xff09;ModuleNotFoundError: No module named ‘Workbook‘ 文章目录 报错代码报错翻译报错原因解决方法千人全栈VIP答疑群联系博主帮忙解决报错 报错代码 粉丝群里的一个小伙伴&#xff0c;想用xlwt库写入Excel表格的…

【干货】Python:load_workbook用法(持续更新)

【干货】Python中load_workbook用法&#xff08;持续更新&#xff09; 功能方法示例文件模块读取导入excel表格获取Sheet查看行与列查看单元格批量访问数据 功能 读取excel文件&#xff0c;并进行操作 方法 示例文件 本文用到的示例文件为一个excel表格&#xff0c;含有名为…

【Python_笔记】openpyxl中Workbook()和.load_workbook()区别

记录学习旅程~ 欢迎各位道友指教与交流~ 准备开始实操时发现一个问题&#xff0c;即Workbook()是创建一个类对象&#xff0c;而load_workbook()是一个方法加载存在的excel。 由于在学习openpyxl时吸取了不同教程&#xff0c;按照读取→编辑→再编辑→保存的逻辑列举了常用的基…

POI(excel)中WorkBook和Sheet应用实践总结

WorkBook定义 工作簿的高级表现形式&#xff0c;是sheet的上级对象。一个excel就是一个工作簿&#xff0c;一个工作簿含有多个工作表(sheet)。 【1】WorkBook两种创建形式 ① 使用直接对象 // 03版本 .xls org.apache.poi.hssf.usermodel.HSSFWorkbook or //07版本 .xls…

Java多线程:Executors

Executors类位于java.util.concurrent包下&#xff0c;提供了一些方便构建ThreadPoolExecutor和线程管理的方法。 主要方法有以下几个&#xff1a; 1.创建一个固定大小的线程池 public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecu…

【Java多线程】线程池【ExecutorServicec和Executors】

➤ Java多线程编程【一文全解】 背景&#xff1a;经常创建和销毁、使用量特别大的资源&#xff0c;比如并发情况下的线程&#xff0c;对性能影响很大&#xff1b; 思路&#xff1a;提前创建好多个线程&#xff0c;放入线程池中&#xff0c;使用时直接获取&#xff0c;使用完放…

为什么不建议用Executors创建线程池???

来源 | www.cnblogs.com/zjfjava/p/11227456.html 01、通过Executors创建线程池的弊端02、通过ThreadPoolExecutor创建线程池03、ThreadPoolExecutor参数解释04、corePoolSize & maximumPoolSize05、keepAliveTime & unit06、等待队列07、拒绝策略08、ThreadPoolExecut…

Executors和ThreadPoolExecutor详解

概述 在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供&#xff0c;不允许在应用中自行显示的创建线程&#xff0c;这样一方面是线程的创建更加规范&#xff0c;可以合理控制开辟线程的数量&#xff1b;另一方面线程的细节管理交给线程池处理&#xff0c;优化了资…

4.Executor执行器

1.Executor 主体结构 Executor是MyBatis执行者接口&#xff0c;执行器的功能包括&#xff1a; 基本功能&#xff1a;改、查&#xff0c;没有增删的原因是&#xff0c;所有的增删操作都可以归结到改。缓存维护&#xff1a;这里的缓存主要是为一级缓存服务&#xff0c;功能包括…