drools规则引擎的实际开发场景
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
引入决策表
决策表格
通过决策表生成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) }
//决策表格
将决策表解析成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
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接