目前市面上比较热门的规则引擎有几款:
- Ilog JRules 是最有名的商用BRMS;
- Drools 是最活跃的开源规则引擎;
- Jess 是Clips的java实现,就如JRuby之于Ruby,是AI系的代表;
- Visual Rules(旗正规则引擎)国内商业规则引擎品牌。
本文将着重介绍Drools。
一 为什么要使用规则引擎
有如下案例,充值得积分的案例 ,规则如下:
充值金额及所得积分的分数原价金额
100以下, 不加分;
100-500 加100分;
500-1000 加500分;
1000 以上 加1000分;
传统java业务实现如下:
public class JavaScoreExample {
public static void main(String[] args) throws Exception {
List<Order> orderList = getInitData();
for (int i=0; i<orderList.size(); i++){
Order order = orderList.get(i);
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);
}
}
}
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<Order>();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
{
Order order = new Order();
order.setAmout(80);
order.setBookingDate(df.parse("2020-11-01"));
User user = new User();
user.setLevel(1);
user.setName("Name1");
order.setUser(user);
orderList.add(order);
}
{
Order order = new Order();
order.setAmout(200);
order.setBookingDate(df.parse("2020-11-01"));
User user = new User();
user.setLevel(2);
user.setName("Name2");
order.setUser(user);
orderList.add(order);
}
return orderList;
}
}
这时候由于市场需求变化,又要调整规则时候,则又要进行业务层面的代码修改、部署,十分麻烦。如果我们可以把决策规则从应用程序中分离出来,将对系统提供很大的便利!
例如 下面业务变更
100 以下 加10分;
100-500 加100分;
500-1000 加500分;
1000 以上 加1000分;
由此,诞生了规则引擎!如下如所示:
规则引擎优势:
对系统的使用人员
- 把业务策略(规则)的创建、修改和维护的权利交给业务经理
- 提高业务灵活性
- 加强业务处理的透明度,业务规则可以被管理
- 减少对IT人员的依赖程度
- 避免将来升级的风险
对IT开发人员
- 简化系统架构,优化应用
- 提高系统的可维护性和维护成本
- 方便系统的整合
- 减少编写“硬代码”业务规则的成本和风险
二、什么是Drools
Drools 是一个基于Charles Forgy’s的RETE算法的,易于访问企业策略、易于调整以及易于管理的开源业务规则引擎,符合业内标准,速度快、效率高。
业务分析师人员或审核人员可以利用它轻松查看业务规则,从而检验是否已编码的规则执行了所需的业务规则。
Drools 是用Java语言编写的开放源码规则引擎,使用Rete算法对所编写的规则求值。Drools允许使用声明方式表达业务逻辑。可以使用非XML的本地语言编写规则,从而便于学习和理解。并且,还可以将Java代码直接嵌入到规则文件中,这令Drools的学习更加吸引人。
三、Drools 实战
下面我们将展示测试项目中使用drools的一个案例 数字匹配操作
- 循环打印 1 到 50,
- 是 5 的倍數就印 five,
- 是 7 的倍數就印 seven,
- 同時是 5 和 7 的倍數就印 five and seven,
- 否则打印数字本身
1、引入核心依赖jar包
<!-- Drools 规则引擎相关依赖 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.44.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.44.0.Final</version>
</dependency>
2、定义controller层接口
/**
* 循环打印 1 到 50,
* 是 5 的倍數就印 five,
* 是 7 的倍數就印 seven,
* 同時是 5 和 7 的倍數就印 five and seven,
* 否则打印数字本身
*
* @author Alnex
* @date: 2020/11/3 9:05
*/
@RestController
@RequestMapping("number")
public class NumberGameController {
@GetMapping("/")
public Object numberGame() {
// KieServices is the factory for all KIE services
KieServices kieServices = KieServices.Factory.get();
// From the kie services, a container is created from the classpath
KieContainer kieContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = null;
// 初始化数据
List<Number> lists = getDataInit();
for (Number number : lists) {
// From the container, a session is created based on
// its definition and configuration in the META-INF/kmodule.xml file
kieSession = kieContainer.newKieSession("numberGame");
kieSession.insert(number);
kieSession.fireAllRules();
System.out.println("第" + number.getNumber() + "条数据的匹配数据为" + number.getStyle());
}
assert kieSession != null;
kieSession.dispose();
return "规则匹配完成";
}
private List<Number> getDataInit() {
List<Number> list = new ArrayList<>();
Number number = null;
for (int i = 0; i < 50; i++) {
number = new Number();
number.setNumber(i);
list.add(number);
}
return list;
}
}
3、定义rule规则
package rules.numbers;
import com.alnex.drools.pojo.Number
rule "number is native matching native"
lock-on-active true
no-loop true
salience 1
when
$number : Number(number != null ,(number % 5) != 0 ,(number % 7) != 0)
$style : Number(style == null)
then
$style.setStyle($number.getNumber().toString());
end
rule "number is five times matching five"
lock-on-active true
no-loop true
salience 1
when
$number : Number(number != null ,(number % 5) == 0 ,(number % 7) != 0)
$style : Number(style == null)
then
$style.setStyle("five");
end
rule "number is seven times matching seven"
lock-on-active true
no-loop true
salience 1
when
$number : Number(number != null ,(number % 5) != 0 ,(number % 7) == 0)
$style : Number(style == null)
then
$style.setStyle("seven");
end
rule "number is five times and seven times matching five and seven,"
lock-on-active true
no-loop true
salience 1
when
$number : Number(number != null ,(number % 5) == 0 ,(number % 7) == 0)
$style : Number(style == null)
then
$style.setStyle("five and seven");
end
4、定义kmodule文件
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="numberGame" packages="rules.numbers">
<ksession name="numberGame"/>
</kbase>
</kmodule>
5、启动项目 执行相关接口 [ 部分数据示例 ]
第26条数据的匹配数据为26
第27条数据的匹配数据为27
第28条数据的匹配数据为seven
第29条数据的匹配数据为29
第30条数据的匹配数据为five
第31条数据的匹配数据为31
第32条数据的匹配数据为32
第33条数据的匹配数据为33
第34条数据的匹配数据为34
第35条数据的匹配数据为five and seven
四、Drools规则属性
1、No-loop
作用:防止规则通过update之类的函数修改了Fact对象时,可能使规则再次被激活,从而导致死循环。
什么时候用?
当被修改的事实对象与规则LHS部分的约束条件为包含关系时。例如
rule No-loop1
no-loop true
when
$p:Person(name=="张三", age==30);
then
$p.setAge(50);
update($p);
System.out.println("设置no-loop时的效果");
end
否则,设置了no-loop为true也会出现死循环,例如
rule No-loop2
no-loop true
when
$p:Person(name=="张三");
then
$p.setAge(50);
update($p);
System.out.println("设置no-loop时的效果");
end
2、lock-on-active
作用:no-loop的升级版,一个更强大的解决死循环的属性。当规则提设置该属性为True时,则当前只会被触发一次。无论如何更新规则事实对象,当前规则也只能被触发一次。
例如
rule lock-on-active
lock-on-active true
when
$p:Person(name=="张三");
then
$p.setAge(50);
update($p);
System.out.println("设置no-loop时的效果");
end
3、salience
作用:如果不设置salience属性,规则体的执行顺序为由上到下,否者,salience值越大,执行顺序越高。
例如:
rule "salience1"
salience 10
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "salience2"
salience 5
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
4、enabled
作用:指规则是否可以被执行,若规则体设置为 enabled false,即将规则体视为永不激活。
例如:
rule "enabled1"
enabled true
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "enabled2"
enabled false
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
5、dialect
作用:用来定义规则中要使用的语言类型,支持Mvel和Java两种类型的语言,默认情况下由包指定。
6、date-effective
作用:只有当前系统时间大于等于设置的时间或者日期,规则才会被激活。
例如:
rule "date-effective"
date-effective "07-August-2018"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
7、date-expires
作用:与date-effective相反,只有当前系统时间小于设置的时间或者日期,规则才会被激活。
8、duration
作用:表示定时器,如果当前规则LHS部分为True,规则继续执行。如果该属性已经被弃用,那么通过新的属性timer来控制。
9、activation-group
作用:activation-group指激活分组,通过字符串定义分组名称,具有相同组名名称的规则体有且只有一个规则被激活,其他规则体的LHS部分仍然为true也不会被执行。该属性受salience属性的影响,如当前规则文件中的其他规则未设计该属性,则视为规则处于被激活状态,并不受该属性的影响。
rule "activation-group1"
salience 10
activation-group "testAgs"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "activation-group2"
salience 10
activation-group "testAgs"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "activation-group3"
activation-group "testAgs"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "activation-group4"
activation-group "testAgs"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
10、agenda-group
作用:agenda-group是议程分组,属于另一种可控的规则执行方式,是指用户可以通过配置agenda-group的参数来控制规则的执行,而且只有获取焦点的规则才会被激活。
例如:ks.getAgenda().getAgendaGroup(“ag1”).setFocus();
rule "agenda-group1"
agenda-group "ag1"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "agenda-group2"
agenda-group "ag2"
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
如果有两个规则体的agenda-group属性相同,都可以被激活
agenda-group | activation-group | 结果说明 | |
是否同一Focus | 是 | 是 | 只会执行其中一个规则。执行顺序根据优化级控制,默认为从上到下 |
否 | 是 | 只有获取焦点的规则才会被激活 | |
是 | 否 | 会执行多个规则,但只有获取Focus的规则才会被激活 | |
否 | 否 | 只有获取焦点的规则才会被激活 | |
11、auto-focus
作用:auto-focus 属性为自动获取焦点,即当前规则是否被激活,如果一个规则被执行,那么认为auto-focus为true;如果单独设置,一般结合agenda-group。当一个agenda-group没有获取焦点时,可以用auto-focus来控制。
12、timer
作用:用来控制规则的执行时间。
用法:
①timer(int:30s) timer(int:30s 5m)
②timer(cron:0/1****?)
③timer(int:30s 10s; start=3-JAN-2018,end=5-JAN-2018)
示例:
rule "timer1"
timer(int:3s) //每三秒执行一次
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
rule "timer2"
timer(cron:0/1****?) //每一秒执行一次
when
//这里如果为空,则表示eval(true)
then
System.out.println("hello word");
end
项目地址
项目源码获取方式,识别下方二维码,回复 【drools】即可获取下载链接