A场景引入

公司开发充值发放优惠券活动,具体规则如下:

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,可以实现热加载

规则引擎的简易demo

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;

规则引擎的关键字与api

  • 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 开启定时任务

规则集引擎的扩展(sliding window):可以实现时间的控制与限流操作

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

引入决策表

决策表格

drools规则引擎的实际开发场景插图

通过决策表生成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<String, String> amountMap = new HashMap<>();

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)

}

//决策表格

drools规则引擎的实际开发场景插图1

将决策表解析成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<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);

for (Message message : messages) {
System.out.println("Error: "+message.getText());

}

throw new IllegalStateException("Compilation errors were found. Check the logs.");

}

return kieHelper.build().newKieSession();

}

版权声明:本文为CSDN博主「gudaichaoren」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gudaichaoren/article/details/90741599



drools规则引擎的实际开发场景插图2

关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台

除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接

本文链接:https://choupangxia.com/2020/11/28/drools/