drools官网:https://www.drools.org/
droolsgithub:https://github.com/kiegroup/drools
comma开发充值发放优惠券活动,具体规则如下:
100元,送10元优惠券·
200元,送25元优惠券
300元,送40元优惠券
Java后端攻城师在代码利用if-else代码将业务逻辑实现了功能,这样看似完全没有必要引入什么鬼规则引擎;
但问题出现了:几天后业务人员发现充值的人还是很少,就想修改发放优惠券活动:100元送15元优惠券等......
这时候攻城师忍气吞声修改后端代码,并经过一大堆发布流程进行上线;
一段时间过后客户量多了,业务人员评估后有要减少优惠券的发放金额.......这时候,一场硝烟滚滚而来
这时候处理这样的业务需求:规则集就可以隆重登场了。
1,什么是规则引擎
规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。
2,为什么我们要使用规则引擎
1,对于我们的业务规则的匹配而言。以传统的命令式编码(即我们java代码条件判断等),如果业务规则发生了变化,我们需要重新从头缕逻辑,然后在合适的地方添加/删除、修改我们的条件判断。这带来就是业务逻辑的捆绑以及不可预知的Bug。而Drools提倡的是声明式编程,我们只需要像做流水线一样,单独做自己的业务规则即可,每个业务规则均是独立的,类似的,可以想象为if/else if/else变为了插拔式的多个if判断。
2,通过使用决策表,决策表是excel表格,业务人员将业务逻辑写入决策表中,后端开发可以读取决策表得到业务逻辑,这样可以达到业务与开发分离,将复杂的业务交给业务人员处理。
3,可以实现热加载
drl文件
package rules
import com.neo.drools.HelloWorldExample.Message;
global java.util.List list
rule "Hello World"
dialect "mvel"
when
m : Message( status == Message.HELLO)
then
System.out.println( m.getMessage () );
modify ( m ) { message = "Goodbye cruel world",
status = Message.GOODBYE };
end
rule "Good Bye"
dialect "java"
when
m: Message( status == Message.GOODBYE)
then
System.out.println(m.getMessage ());
end
=============================================
java代码
public static final void main(final String[] args) {
KieServices ks = KieServices.Factory.get();
KieContainer kc = ks.getKieClasspathContainer();
execute( kc );
}
public static void execute( KieContainer kc ) {
KieSession ksession = kc.newKieSession("HelloWorldKS");
final Message message = new Message();
message.setMessage( "Hello World" );
message.setStatus( Message.HELLO );
ksession.insert( message );
ksession.fireAllRules();
ksession.dispose();
}
public static class Message {
public static final int HELLO = 0;
public static final int GOODBYE = 1;
private String message;
private int status;
package 与Java语言类似,drl的头部需要有package和import的声明,package不必和物理路径一致。
import 导出java Bean的完整路径,也可以将Java静态方法导入调用。
rule 规则名称,需要保持唯一 件,可以无限次执行。
no-loop 定义当前的规则是否不允许多次循环执行,默认是 false,也就是当前的规则只要满足条件,可以无限次执行。
lock-on-active 将lock-on-active属性的值设置为true,可避免因某些Fact对象被修改而使已经执行过的规则再次被激活执行。
salience 用来设置规则执行的优先级,salience 属性的值是一个数字,数字越大执行优先级越高, 同时它的值可以是一个负数。默认情况下,规则的 salience 默认值为 0。如果不设置规则的 salience 属性,那么执行顺序是随机的。
when 条件语句,就是当到达什么条件的时候
then 根据条件的结果,来执行什么动作
end 规则结束
insert 在我们规则语句中的then语句,规则可以推断出新的信息。更多的数据可以提供给工作中的内存,以便对使用insert关键字的所有规则进行进一步的评估
modify 修改工作中内存的值,所有规则进行进一步的评估
timer 开启定时任务
package rules;
dialect "mvel"
declare Double
@role( event )
end
rule "test02"
when
//10s钟后处理fact
$d : Double() over window:time(10s)
then
System.out.println($d);
end
rule "test02 - 01"
when
//处理最后2个fact
$d : Double() over window:length(2)
then
System.out.println($d);
end
决策表格
——————————————————————————————————————————————
通过决策表生成drl文件
package data;
//generated from Decision Table
import org.drools.decisiontable.Person;
// rule values at B11, header at B6
rule "Spreadsheet Example_11"
when
$person: Person(Age >= 0 && Age < 18)
then
$person.setCanBuyAlcohol(false);
end
// rule values at B12, header at B6
rule "Spreadsheet Example_12"
when
$person: Person(Age >= 18 && Age < 150)
then
$person.setCanBuyAlcohol(true);
end
drools实战场景架构:业务人员在决策表进行编写规则,后端开启定时任务去将决策表转化成drl字符串,然后保存到redis,后端代码运行规则时判断redis有没有,有的话就读取用drl生成kiesession,然后获取drl脚本进行获取到kiesession,进而可以进行程序代码执行。
好处:
1.可以实现复杂业务交给业务人员
2,将开启定时任务将drl文本解析到redis中,获取提供个后台管理接口,将决策表格解析的文本保存到redis,降低服务器压力
3,实现代码与业务的分离,热部署业务逻辑
demo如下:
//controller层
@RequestMapping(value = "/test01", method = RequestMethod.GET)
public void test01() throws FileNotFoundException {
Map
ScoreInfo info = new ScoreInfo();
info.setCount(10);
String drl = (String) cacheManager.get("score_sign"); //从redis获取drl脚本
if (drl == null) {
drl = KieSessionUtils.getDRL("C:\\DROOLS\\score_sign.xls"); //将决策表格解析成drl脚本
cacheManager.put("score_sign", drl); //放入redis
}
System.out.println(drl);
KieSession kieSession = KieSessionUtils.createKieSessionFromDRL(drl); //通过drl脚本创建kiesession
kieSession.getAgenda().getAgendaGroup("score_sign").setFocus(); //开启score_sign议程
kieSession.insert(info);
kieSession.setGlobal("amountMap", amountMap); //设置全区变量
kieSession.fireAllRules(); //执行drl
System.out.println("评估规则ok");
String score = amountMap.get("score");
String coupon = amountMap.get("coupon");
System.out.println("获得积分奖励:" + score);
System.out.println("获得美金奖励:" + coupon)
}
//决策表格
将决策表解析成drl脚本方法
// 把xls文件解析为String
public static String getDRL (String realPath) throws FileNotFoundException {
File file = new File(realPath); // 例如:C:\\abc.xls
InputStream is = new FileInputStream(file);
SpreadsheetCompiler compiler = new SpreadsheetCompiler();
return compiler.compile(is, InputType.XLS);
}
使用drl脚本创建kiession方法
// drl为含有内容的字符串
public static KieSession createKieSessionFromDRL(String drl) {
KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
List
for (Message message : messages) {
System.out.println("Error: "+message.getText());
}
throw new IllegalStateException("Compilation errors were found. Check the logs.");
}
return kieHelper.build().newKieSession();
}