规则引擎,全称为业务规则管理系统,英文名为BRMS(即Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。
需要注意的是规则引擎并不是一个具体的技术框架,而是指的一类系统,即业务规则管理系统。目前市面上具体的规则引擎产品有:drools、VisualRules、iLog等。
规则引擎实现了将业务决策从应用程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。规则引擎其实就是一个输入输出平台。
上面的申请信用卡业务场景使用规则引擎后效果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2XMP5Ou6-1637305115031)(…/media/pictures/Drools.assets/4.png)]
系统中引入规则引擎后,业务规则不再以程序代码的形式驻留在系统中,取而代之的是处理规则的规则引擎,业务规则存储在规则库中,完全独立于程序。业务人员可以像管理数据一样对业务规则进行管理,比如查询、添加、更新、统计、提交业务规则等。业务规则被加载到规则引擎中供应用系统调用。
使用规则引擎的优势如下:
1、业务规则与系统代码分离,实现业务规则的集中管理
2、在不重启服务的情况下可随时对业务规则进行扩展和维护
3、可以动态修改业务规则,从而快速响应需求变更
4、规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则
5、减少了硬编码业务规则的成本和风险
6、使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单
对于一些存在比较复杂的业务规则并且业务规则会频繁变动的系统比较适合使用规则引擎,如下:
1、风险控制系统----风险贷款、风险评估
2、反欺诈项目----银行贷款、征信验证
3、决策平台系统----财务计算
4、促销平台系统----满减、打折、加价购
drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。
drools官网地址:https://drools.org/
drools源码下载地址:https://github.com/kiegroup/drools
SpringBoot整合规则引擎Drools:https://blog.csdn.net/weixin_48658389/article/details/121348659
drools规则引擎由以下三部分构成:
Working Memory:工作内存,drools规则引擎会从Working Memory中获取数据并和规则文件中定义的规则进行模式匹配,所以我们开发的应用程序只需要将我们的数据插入到Working Memory中即可,例如本案例中我们调用kieSession.insert(order)就是将order对象插入到了工作内存中。
Fact:事实,是指在drools 规则应用当中,将一个普通的JavaBean插入到Working Memory后的对象就是Fact对象,例如本案例中的Order对象就属于Fact对象。Fact对象是我们的应用和规则引擎进行数据交互的桥梁或通道。
Rule Base:规则库,我们在规则文件中定义的规则都会被加载到规则库中。
Pattern Matcher:匹配器,将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,匹配成功的规则将被激活并放入Agenda中。
Agenda:议程,用于存放通过匹配器进行模式匹配后被激活的规则。
Execution Engine:执行引擎,执行Agenda中被激活的规则。
规则文件的构成:
在使用Drools时最重要的工作就是编写规则文件,规则文件的后缀为.drl。
drl是Drools Rule Language的缩写。在规则文件中编写具体的规则内容。
一套完整的规则文件内容构成如下:
关键字 | 描述 |
---|---|
package | 包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用 |
import | 用于导入类或者静态方法 |
global | 全局变量 |
function | 自定义函数 |
query | 查询 |
rule end | 规则体 |
Drools语法结构
rule "ruleName"
attributes
when
LHS
then
RHS
end
主要关键字说明
rule:关键字,表示规则开始,参数为规则的唯一名称。
attributes:规则属性,是rule与when之间的参数,为可选项。
when:关键字,后面跟规则的条件部分。
LHS(Left Hand Side):是规则的条件部分的通用名称。它由零个或多个条件元素组成。如果LHS为空,则它将被视为始终为true的条件元素。 (左手边)
then:关键字,后面跟规则的结果部分。
RHS(Right Hand Side):是规则的后果或行动部分的通用名称。 (右手边)
end:关键字,表示一个规则结束。
Drools中的匹配器可以将Rule Base中的所有规则与Working Memory中的Fact对象进行模式匹配,我们需要在规则体的LHS部分定义规则并进行模式匹配。LHS部分由一个或者多个条件组成,条件又称为pattern。
pattern的语法结构为:绑定变量名:Object(Field约束),绑定变量既可以用在对象上,也可以用在对象的属性上
其中绑定变量名可以省略,通常绑定变量名的命名一般建议以$开始。如果定义了绑定变量名,就可以在规则体的RHS部分使用此绑定变量名来操作相应的Fact对象。Field约束部分是需要返回true或者false的0个或多个表达式。
LHS部分可以定义多个pattern,多个pattern之间可以使用and或者or进行连接,也可以不写,默认连接为and。
Drools提供的比较操作符,如下表:
符号 | 说明 |
---|---|
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
contains | 检查一个Fact对象的某个属性值是否包含一个指定的对象值 |
not contains | 检查一个Fact对象的某个属性值是否不包含一个指定的对象值 |
memberOf | 判断一个Fact对象的某个属性是否在一个或多个集合中 |
not memberOf | 判断一个Fact对象的某个属性是否不在一个或多个集合中 |
matches | 判断一个Fact对象的属性是否与提供的标准的Java正则表达式进行匹配 |
not matches | 判断一个Fact对象的属性是否不与提供的标准的Java正则表达式进行匹配 |
contains | not contains语法结构
Object(Field[Collection/Array] contains value)
Object(Field[Collection/Array] not contains value)
//测试比较操作符contains
rule "comparison_rule_1"
when
ComparisonOperatorEntity(name contains "张三")
ComparisonOperatorEntity(data contains name)
then
System.out.println("张三规则触发");
end
//测试比较操作符not contains
rule "comparison_rule_2"
when
ComparisonOperatorEntity(name not contains "张三")
ComparisonOperatorEntity(data not contains name)
then
System.out.println("not张三规则触发");
end
memberOf | not memberOf语法结构
Object(field memberOf value[Collection/Array])
Object(field not memberOf value[Collection/Array])
//测试比较操作符memberOf
rule "comparison_rule_3"
when
ComparisonOperatorEntity(name memberOf data)
then
System.out.println("memberOf规则");
end
//测试比较操作符memberOf
rule "comparison_rule_4"
when
ComparisonOperatorEntity(name not memberOf data)
then
System.out.println("not memberOf规则");
end
matches | not matches语法结构
Object(field matches “正则表达式”)
Object(field not matches “正则表达式”)
//测试比较操作符matches(后面匹配正则表达式)
rule "comparison_rule_5"
when
ComparisonOperatorEntity(name matches "王.*")
then
System.out.println("matches规则");
end
//测试比较操作符matches(后面匹配正则表达式)
rule "comparison_rule_6"
when
ComparisonOperatorEntity(name not matches "王.*")
then
System.out.println("not matches规则");
end
contain是前面包含后面,memberOf是后面包含前面。
Drools的关键字分为:硬关键字(Hard keywords)和软关键字(Soft keywords)。
硬关键字是我们在规则文件中定义包名或者规则名时明确不能使用的,否则程序会报错。软关键字虽然可以使用,但是不建议使用。
硬关键字包括:true false null
软关键字包括:lock-on-active date-effective date-expires no-loop auto-focus activation-group agenda-group ruleflow-group entry-point duration package import dialect salience enabled attributes rule extend when then template query declare function global eval not in or and exists forall accumulate collect from action reverse result end over init
规则文件的RHS
部分的主要作用是通过插入,删除或修改工作内存中的Fact数据,来达到控制规则引擎执行的目的。Drools提供了一些方法可以用来操作工作内存中的数据,**操作完成后规则引擎会重新进行相关规则的匹配,**原来没有匹配成功的规则在修改数据完成后有可能就会匹配成功。
update方法
update方法的作用是更新工作内存中的数据,并让相关的规则重新匹配。 可能会造成死循环问题
//当前规则用于测试内置update方法
rule "age less then 10"
when
$student:Student(age < 10)
then
$student.setAge(15);
update($student)//update方法用于修改fact对象,会导致相关规则重新匹配
System.out.println("age less then 10 触发");
end
rule "age less then 20 and greater than 10"
when
$student:Student(age < 20 && age > 10)
then
//$student.setAge(15);
System.out.println("age less then 20 and greater than 10 触发");
end
insert方法
insert方法的作用是向工作内存中插入数据,并让相关的规则重新匹配。
//当前方法用来处理insert内置方法
rule "age == 10"
when
$student:Student(age == 10)
then
Student stu = new Student();
stu.setAge(5);
insert(stu);//insert方法作用是向工作内容中插入fact对象,会导致相关规则重新匹配
System.out.println("age == 10 触发");
end
retract方法
retract方法的作用是删除工作内存中的数据,并让相关的规则重新匹配。
//当前规则用来测试内置的retract方法
rule "age equal 10"
salience 10 //指定规则的优先级
when
$student:Student(age == 10)
then
retract($student);//retract方法作用是删除工作内容中的fact对象,会导致相关规则重新匹配
System.out.println("age equal 10 触发");
end
介绍规则体构成中的attributes部分 Drools中提供的属性如下表(部分属性):
属性名 | 说明 |
---|---|
salience | 指定规则执行优先级 |
dialect | 指定规则使用的语言类型,取值为java和mvel |
enabled | 指定规则是否启用 |
date-effective | 指定规则生效时间 |
date-expires | 指定规则失效时间 |
activation-group | 激活分组,具有相同分组名称的规则只能有一个规则触发 |
agenda-group | 议程分组,只有获取焦点的组中的规则才有可能触发 |
timer | 定时器,指定规则触发的时间 |
auto-focus | 自动获取焦点,一般结合agenda-group一起使用 |
no-loop | 防止死循环 |
enabled属性:
默认为true,用于指定当前规则是否启用,取值为true或者falsedialect属性:
默认为java,用于指定当前规则使用的语言类型,取值为java或mvelsalience属性:
用于指定规则执行的优先级,取值类型为Integer,数值越大优先级越高,若不设置优先级属性,默认从上往下执行no-loop属性:
用来防止死循环,当规则通过update之类的函数修改了Fact对象时,可能导致当前规则被再次激活导致死循环。取值类型为Boolean,默认为falseactivation-group属性:
用来激活分组,取值为String类型。具有相同分组名称的规则只能有一个规则被触发agenda-group属性:
属性为议程分组,属于另一种可控的规则执行方式。用户可以通过设置agenda-group来控制规则的执行,只有获得焦点的组中的规则才能触发。auto-focus属性:
自动获取焦点,取值类型为Boolean,默认为false。一般结合agenda-group属性使用,当一个议程分组未获得焦点时,可以设置auto-focus属性来控制。(同一个组只要有一个获取焦点将都获得焦点)timer属性:
可以通过定时器的方式指定规则执行的时间,使用方式有两种。timer(int:<initial delay><repeat interval>?)
此种方式遵循Timer对象的使用方式,第一个参数标识几秒后执行,第二个参数表示每隔几秒执行一次,第二个参数为可选。timer(cron:<cron expression>)
此方式使用标准的unix cron表达式的使用方式来定义规则执行的时间。rule "timer_rule_1"
timer(3s 2s)
when
then
System.out.println("触发timer_rule_1规则");
end
rule "timer_rule_2"
timer(cron:0/1 * * * * ?)
when
then
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("触发timer_rule_2规则" + sdf.format(new Date().getTime()));
end
date-effective属性:
用来指定规则的生效时间,即只有当前系统时间大于或等于设置的时间或者日期规则才有可能触发,默认日期格式为:dd-MMM-yyyy,用户也可以自定义日期格式。//当前规则文件用来测试date-effective属性
rule "rule-date-effective-1"
date-effective "2021-11-03 19:45"//用于指定当前规则生效时间
when
then
System.out.println("rule-date-effective-1规则触发,触发的时间为"+ new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()));
end
date-expires属性:
用来指定规则失效的时间,即只有当前系统时间小于设置的时间或者日期规则才有可能触发,默认日期格式为:dd-MMM-yyyy,用户也可以自定义日期格式。rule "rule_date_expires_1"
date-expires "2021-11-03 19:50"
when
then
System.out.println("rule_date_expires_1规则触发,触发的时间为"+ new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()));
end
global全局变量
global:
global关键字用于在规则文件中定义全局变量,他可以让应用程序的对象在规则文件中能够访问。可以用来为规则文件提供数据或服务。语法结构:
global 对象类型 对象名称两点注意
1、如果对象类型为包装类型时,在一个规则中改变了global的值,那么只针对当前规则有效,对其他规则中的global不会有影响,可以理解为他是当前规则代码中的global副本,规则内部修改不会影响全局的使。//测试global全局变量
global Integer count//定义一个包装类型的全局变量
global List gList;//定义个一个集合类型的全局变量
global UserService userService//定义一个JavaBean全局变量
rule "rule_global_1"
when
then
count+=10; //对于包装类型的全局变量的修改只针对当前规则生效
System.out.println("规则rule_global_1触发");
System.out.println("全局变量count计算之后为"+count);
gList.add("chenfeng");//修改集合全局变量
System.out.println("1集合全局变量的size:"+gList.size());
userService.save();
end
rule "rule_global_2"
when
then
System.out.println("规则rule_global_2触发");
System.out.println("全局变量的值为"+count);
System.out.println("2集合全局变量的size:"+gList.size());
userService.save();
end
query查询:
query查询提供了一种查询working memory中符合约束条件的Fact对象的简单方法,他仅包含规则文件中的LHS部分不用知道when和then部分并且以end结束。具体语法如下
query 查询的名称(可选参数)
LHS
END
//当前规则文件用来测试query查询
//定义一个query查询
query "query_1"
$s:Student(age==50)
end
//带有查询参数的query
query "query_2"(String sname)
$s:Student(age > 50 && sname.equals(name))
// $s:Student(age > 50 && name==sname)
end
function关键字:
function关键字用于在规则文件中定义函数,相当于Java类中的方法。可以在规则体中调用定义的函数。使用函数的好处是可以将业务逻辑集中放置在同一个位置,根据需要可以对函数进行修改。语法结构如下:
function 返回值类型 函数名(可选参数){
//逻辑代码
}
//当前规则文件用于测试function自定义函数
function String sayHi(String name){
return name + "HelloWorld";
}
//定义一个规则调用自定义函数
rule "rule_function_1"
when
$student:Student(age == 18)
then
String s = sayHi($student.getName());
System.out.println("自定义函数的结果为"+"("+s+")");
end
LHS加强:
在规则体中LHS部分是介于when和then之间的部分,主要用于模式匹配,只有匹配结果为true时,才会触发RHS部分的执行。
复合值限制(in/not in):
复合值限制是指超过一种匹配值的条件,类似Sql中的in关键字,在drools规则体中的LHS部分可以使用in或not in进行复合值匹配,语法结构如下Object(field in(比较值1,比较值2....))
rule "rule_lhs_1"
when
$s:Student(name in ("张三","李四","王五"))
then
System.out.println("规则rule_lhs_1触发。");
end
rule "rule_lhs_2"
when
$s:Student(name not in ("张三","李四","王五"))
then
System.out.println("规则rule_lhs_2触发。");
end
条件元素eval:
用于规则体的LHS部分,并返回一个Boolean型的值。语法结构如下:
eval(表达式)
eval(true) eval(false) eval(1==1)
条件元素not
用于判断Working Memory(工作空间)中是否存在某个Fact对象,如果不存在则返回true,如果存在则返回false,语法结构如下
not object(可选属性约束) eg:not Student(); not Student(age<10)
rule "rule_lhs_3"
when
not Student()
then
System.out.println("规则rule_lhs_3触发。");
end
条件元素exists
与not作用相反,用于判断Working Memory(工作空间)中是否存在某个Fact对象,如果存在则返回true,如果不存在则返回false,语法结构如下exists object(可选属性约束) eg:exists Student(); exists Student(age<10)
//使用exists规则
rule "rule_lhs_4"
when
exists Student()
then
System.out.println("规则exists触发。");
end
与不加的区别:当向Working Memory中加入多个满足条件的Fact对象时,使用了exists的规则执行一次,不使用exists的规则会执行多次。
规则继承(extends):
规则之间可以使用extends关键字进行规则条件部分的继承,类似与Java之间的继承。RHS部分加强:
RHS是规则体的重要组成部分,当LHS部分的条件匹配成功之后,对应的RHS部分就会触发执行,一般在RHS部分进行业务处理,在RHS部分Drools为我们提供了一个内置对象:drools,下面介绍这个内置对象的用法。
halt方法:
halt方法的作用是立即终止后面所有规则的执行。getWorkingMemory方法:
getWorkingMemory方法的作用是返回工作内存对象。getRule方法:
getRule方法的作用是返回规则对象。//用来测试RHS部分drools内置对象的相关方法
rule "rule_drools_1"
when
then
System.out.println("规则rule_drools_1触发了");
WorkingMemory workingMemory = drools.getWorkingMemory();//获得工作内存对象,本质是一个会话session对象。
System.out.println(workingMemory);
org.drools.core.definitions.rule.impl.RuleImpl rule = drools.getRule();//获得规则对象
System.out.println(rule);
drools.halt();//当前方法会终止后面所有规则的执行
end