规则引擎:
全称:业务规划管理系统(business-rule-management-system)BRMS
规则引擎的主要思想是将引用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),
由用户或开发者在需要时进行配置、管理。 需要注意的是规则引擎并不是一个具体的技术框架,而是指的一类系统,
即业务规则管理系统。
目前市面上具体的规则引擎产品有:drools、VisualRules、ilog等,使用最为广泛并且开源的是Drools。
规则引擎实现了将业务决策从应用程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。
规则引警其实就是一个输入输出平台。
规则引擎主要应用场景
对于一些存在比较复杂的业务规则并且业务规则会频繁变动的系统比较适合使用规则引警,如下:
1、风险控制系统----风险贷款、风险评估
2、反欺诈项目----银行贷款、征信验证
3、决策平台系统----财务计算
4、促销平台系统----满减、打折、加价购
Drools介绍
drools是一款由JBoss组织提供的基于Java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,
以规则脚本的形式存放在文件或特定的存储介质中(例如存放在数据库中),
使得业务规则的变更不需要修改项目代码、不用重启服务器就可以在线上环境立即生效。
drools官网地址:https://drools.org/
drools源码下载地址:https://github.com/kiegroup/drools
使用规则引擎能够解决什么问题?针对复杂的业务规则代码处理,往往存在一下问题:
1、硬编码实现业务规则难以维护;
2、硬编码实现业务规则难以应对变化;
3、业务规则发生变化需要修改代码,重启服务后才能生效;于是规则引擎Drools便诞生在项目中。
使用规则引擎的优势如下:
1、业务规则与系统代码分离,实现业务规则的集中管理
2、在不重启服务的情况下可随时对业务规则进行扩展和维护
3、可以动态修改业务规则,从而快速响应需求变更
4、规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则
5、减少了硬编码业务规则的成本和风险
6、使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单
在项目中使用drools时,即可以单独使用也可以整合spring使用。如果单独使用只需要导入如下maven坐标即可:
Drools API 开发步骤
获取KieServices
->
获取KieContainer
->
KieSession
->
Insert fact
->
触发规则
->
关闭KieSession
使用Drools
- 引入依赖
<!-- drools -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.10.0.Final</version>
</dependency>
- 创建kmodule.xml
位置> resources >META-INF>kmodule.xml
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<!--
name:指定kbase的名称,可以任意,但是需要唯一
packages:指定规则文件的目录,需要根据实际情况境写,否则无法加载到规则文件
default:指定当前kbase是否为默认
-->
<kbase name="orderKbase" packages="drools" default="true">
<!--
name:指定ksession名称,可以任意,但是需要唯一
default:撒定当前session是否为默认
-->
<ksession name="ksession-rule" default="true"/>
</kbase>
</kmodule>
- 创建规则文件drl
位置 resources>drools
package drools; //这个包名要和 kmodule.xml 中的 packages="drools" 一致
import com.jason.drools.Order;
/*
--规则文件的构成
drl是Drools Rule Language的缩写。在规则文件中编写具体的规则内容
一套完整的规则文件内容构成如下:
。package:包名,package对应的不一定是真正的目录,可以任意写com.abc,同一个包下的dr文件可以相互访问
。import:用于导入类或者静态方法
。global:全局变量
。function:自定义函数
。query:查询
。rule end:规则体
*/
//默认执行
rule "score_always"
when
//不给条件,返回eval(ture),表示条件成立
then
System.out.println("执行rule");
end
//省略实参
rule "score_2"
when
// $order:Order(amount<100)
// 如果后面不会使用参数,可省略赋值
Order(amount<100)
then
System.out.println("执行rule: score_2");
end
/*
案例
100元以下,不加分
100元-500元加100分
500元-1000元加500分
1000元以上加1000分
*/
//100元以下,不加分
rule "score_100"
when
//给order赋值
$order:Order(amount<100)
then
$order.setScore(0);
System.out.println("100元以下,不加分");
end
//100元-500元加100分
rule "score_500"
when
// && 与 , 作用相同
// $order:Order(100 < amount && amount<500)
$order:Order(100 < amount , amount<500)
then
$order.setScore(100);
System.out.println("100元-500元加100分");
end
//500元-1000元加500分
rule "score_1000"
when
$order:Order(500 < amount && amount<1000)
then
$order.setScore(500);
System.out.println("500元-1000元加500分");
end
//1000元以上加1000分
rule "score_1000_2"
when
$order:Order(amount>1000)
then
$order.setScore(1000);
System.out.println("1000元以上加1000分");
end
- 测试
@Test
public void test(){
Order order = new Order();
order.setAmount(399);
orderRuleFn(order);
}
public void orderRuleFn(Order order){
/*
Kie全称为Knowledge is Everything,即”知识就是一切”的缩写,是Jboss一系列项目的总称
--规则引擎构成
drools规则引擎由以下三部分构成:
-Working Memory(工作内存)
-Rule Base(规则库)
-Inference Engine(推理引擎)
其中Inference Engine(推理引擎)又包括:
-Pattern Matcher(匹配器)具体匹配哪一个规则,由这个完成
-Agenda(议程)
-Execution Engine(执行引擎)
*/
KieSession kieSession = null;
try {
// 1 获取KieServices
KieServices kieServices = KieServices.Factory.get();
// 2 获取KieContainer
KieContainer kieContainer = kieServices.getKieClasspathContainer();
// 3 KieSession (用于规则引擎交互
kieSession = kieContainer.newKieSession();
// 4 Insert fact(将数据交给规则引擎,规则引擎根据提供的数据进行规则匹配
kieSession.insert(order);
// 5 触发规则(匹配成功执行规则
kieSession.fireAllRules();
System.out.println("单次消费获得积分"+order.getScore());
}catch (Exception e){
e.printStackTrace();
}finally {
// 6关闭KieSession
kieSession.dispose();
}
}
以上就是drools最基础的使用方式
drools属性
- salience 指定规则执行优先级
- dialect 指定规则使用的语言类型,取值为java和mvel
- enabled 指定规则是否启用
- date-effective 指定规则生效时间
- date-expires 指定规则失效时间
- activation-group 激活分组,具有相同分组名称的规则只能有一个规则触发
- agenda-group 议程分组,只有获取焦点的组中的规则才有可能触发
- timer 定时器,指定规则触发的时间
- auto-focus 自动获取焦点,一般结合agenda-group一起使用
- no-loop 防止死循环,防止自己更新规则再次触发
- lock-on-active no-loop增强版本。可防止别人更新规则再次出发
示例:
rule "attr_1"
//salience 1
when
//不给条件,返回eval(ture),表示条件成立
then
System.out.println("执行 attr_1");
end
rule "attr_2"
//salience 10
when
then
System.out.println("执行 attr_2");
end
//时间大于指定时间才会触发
rule "attr_date"
date-effective "27-Aug-2024"
//date-effective "2024-08-30"
when
then
System.out.println("执行 attr_date");
end
比较操作符
在 Drools当中共提供了十二种类型的比较操作符,分别是:>、>=、<、<=、==、!=、contains、not contains、memberof、not memberof、matches、not matches;
- contains 检查一个Fact对象的某个属性值是否包含一个指定的对象值
- not contains 检查一个Fact对象的某个属性值是否不包含一个指定的对象值
- memberOf 判断一个Fact对象的某个属性是否在一个或多个集合中
- not memberOf 判断一个Fact对象的某个属性是否不在一个或多个集合中
- matches 判断一个Fact对象的属性是否与提供的标准的Java正则表达式进行匹配
- not matches 判断一个Fact对象的属性是否不与提供的标准的Java正则表达式进行匹配
示例:
public void customerRuleFn(Object object,Object Obj2){
KieSession kieSession = null;
try {
// 1 获取KieServices
KieServices kieServices = KieServices.Factory.get();
// 2 获取KieContainer
KieContainer kieContainer = kieServices.getKieClasspathContainer();
// 3 KieSession (用于规则引擎交互
kieSession = kieContainer.newKieSession();
Order order = new Order();
List<Order> list= new ArrayList<>();
list.add(order);
Customer customer = new Customer();
customer.setName("张三");
customer.setOrderList(list);
// 4 Insert fact(将数据交给规则引擎,规则引擎根据提供的数据进行规则匹配
kieSession.insert(object);
kieSession.insert(Obj2);
// 5 触发规则(匹配成功执行规则
kieSession.fireAllRules();
// 匹配指定rule
// kieSession.fireAllRules(new RuleNameEndsWithAgendaFilter("rule_2"));
}catch (Exception e){
e.printStackTrace();
}finally {
// 6关闭KieSession
kieSession.dispose();
}
}
rule "rule_1"
when
$order:Order();
$cus:Customer(orderList contains $order); //调用时需将两个加入工作内存
then
System.out.println("orderList 包含 order");
end
rule "rule_2"
when
Customer(name matches "张.*");
then
System.out.println("Customer is 张某");
end
结果-RHS
在 Drools 当中,在 RHS 里面,提供了一些对当前 Working Memory 实现快速操作的宏函数或对象,
比如insert/insertLogical、update 和retract 就可以实现对当前 Working Memory中的 Fact 对象进行新增、删除或者是修改
//注意:内置方法触发 触发后会重新匹配规则,可能发生死循环
示例:
rule "rule_3"
when
eval(true);//默认触发
then
Customer customer = new Customer();
customer.setName("李四");
// 加入工作内存中
insert(customer);
//内置方法触发 触发后会重新匹配规则
System.out.println("drools 内置方法触发1");
end
rule "rule_4"
when
$cus:Customer(name matches "李.*");
then
System.out.println("drools 内置方法触发2: "+$cus.getName());
end
rule "rule_5"
when
$cus:Customer(name matches "李.*");
then
$cus.setName("张三");
update($cus);
System.out.println("drools 内置方法触发3: "+$cus.getName());
end
rule "rule_6"
when
$cus:Customer(name matches "张.*");
then
retract($cus)
System.out.println("Customer is 张某"+$cus.getName());
end
结合spring-boot使用示例
- 引入依赖
<!-- drools-->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.10.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.10.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>7.10.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>7.10.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.10.0.Final</version>
</dependency>
- 创建配置类
@Configuration
public class DroolsConfig {
// 指定规则文件存放目录
private static final String RULES_PATH = "drools/";
private final KieServices kieServices = KieServices.Factory.get();
// 加载规则文件
// ConditionalOnMissingBean:保证唯一bean
@Bean
@ConditionalOnMissingBean
public KieFileSystem kieFileSystem() throws IOException{
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
Resource[] files = resourcePatternResolver.getResources("classpath*:"+RULES_PATH+"*.*");
String path = null;
for (Resource file: files) {
path = RULES_PATH+file.getFilename();
kieFileSystem.write(ResourceFactory.newClassPathResource(path,"UTF-8"));
}
return kieFileSystem;
}
@Bean
@ConditionalOnMissingBean
public KieContainer kieContainer() throws IOException{
KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
kieBuilder.buildAll();
return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
}
@Bean
@ConditionalOnMissingBean
public KieBase kieBase() throws IOException{
return kieContainer().getKieBase();
}
@Bean
@ConditionalOnMissingBean
public KModuleBeanFactoryPostProcessor kiePostProcessor(){
return new KModuleBeanFactoryPostProcessor();
}
}
- 使用
@Component
public class RuleService {
@Autowired
private KieBase kieBase;
public Order runRule(Order order){
KieSession kieSession = null;
try {
kieSession = kieBase.newKieSession();
kieSession.insert(order);
kieSession.fireAllRules();
}catch (Exception e){
e.printStackTrace();
}finally {
kieSession.dispose();
}
return order;
}
}