简介
轻量级的规则引擎,易于学习的api
简单来说,规则引擎就是一个函数:y=f(x1,x2,…,xn)
将业务代码和业务规则分离,解耦业务决策和业务代码的绑定关系
入门示例
依赖引入
<dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-core</artifactId><version>4.1.0</version>
</dependency><!--规则定义文件格式,支持json,yaml等-->
<dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-support</artifactId><version>4.1.0</version>
</dependency><!--支持mvel规则语法库-->
<dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-mvel</artifactId><version>4.1.0</version>
</dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-spel</artifactId><version>4.1.0</version>
</dependency>
代码示例
public static void main(String[] args) {Rules rules = new Rules();Rule rule = new RuleBuilder().name("test").description("测试").priority(3).when(facts -> facts.get("userName").equals("zhangsan")).then(facts -> System.out.println("this is test action!")).build();rules.register(rule);Facts facts = new Facts();facts.put("userName","zhangsan");DefaultRulesEngine defaultRulesEngine = new DefaultRulesEngine();defaultRulesEngine.fire(rules,facts);}
核心概念
规则(Rule)
由条件和行动构成的推理语句,通俗的讲:当满足某个条件之后,需要做后续的动作(一个或多个动作),一般表示为IF <conditions> THEN <actions>, Rule表达逻辑。一个规则的IF部分称为LHS,THEN部分称为RHS。
除了以上示例方式构建Rule,还可以通过注解构建Rule:
@Rule(name = "repeatRule",description = "重复规则对消息内容去重",priority = 100)
public class RepeatRule {@Conditionpublic boolean evaluate(@Fact("sendMessages") Set<String> sendMessages, @Fact("message") String message) {return sendMessages.contains(message);}@Action(order =0)public void execute(Facts facts) {//}@Action(order = 1)public void breakRules(Facts facts) {facts.put("break",true);facts.put("reason","repeat limit");}}
事实(Facts)
一组用户的输入数据,规则将根据输入数据判断是否满足条件,从而触发规则actions
Facts是一组Fact集合,源码示例如下:
而Fact是一个容器,可以简单的理解成一个Map对象:
执行引擎(RulesEngine)
引擎执行器,一般为推理引擎。Rules使用LHS与事实进行模式匹配。当匹配被找到,Rules会执行RHS即执行逻辑,同时actions经常会改变facts的状态,来推理出期望的结果。
常用的执行引擎即:DefaultRulesEngine
结合mvel使用示例
@Testpublic void mvelExecute() {MVELRule repeatRule = new MVELRule().name("repeat Rule").priority(100).when("!sendMessages.contains(msgId)").then("result.put('break',true);result.put('reason','repeat limit')");Rules rules = new Rules(repeatRule);Facts facts = new Facts();facts.put("msgId","123456");facts.put("sendMessages",new TreeSet<>());facts.put("msgType","interaction");facts.put("message","hello world");facts.put("result",new HashMap<>());DefaultRulesEngine engine = new DefaultRulesEngine();//CustomRuleListener customRuleListener = new CustomRuleListener();//engine.registerRuleListener(customRuleListener);engine.fire(rules,facts);Map<String,Object> result = (Map<String,Object>)facts.get("result");log.info("执行结果是:{}", JSONUtil.toJsonStr(result));}
MVEL可以支持表达式支持常用的逻辑符:> = < && || 等,还支持java类型api的调用
最直观的使用MVEL表达是API如下:
@Testpublic void mvelCompileTest(){MyTest obj = new MyTest();Map<String, Object> map = new HashMap<>();map.put("key1", 1);map.put("key2", 2);String expression = "func(map)";Map<String, Object> vars = new HashMap<>();vars.put("map", map);MVEL.executeExpression(MVEL.compileExpression(expression), obj, vars);System.out.println(map);}public void func(Map<String, Object> map) {map.put("test","lisi");}
详细的MVEL脚本语言的使用:http://mvel.documentnode.com/
结合spel使用示例
public static void main(String[] args) {Rules rules = new Rules();SpELRule spelRule = new SpELRule().name("1").description("1").priority(1).when("#{ ['item'].price >= 100 }").then("#{ ['item'].setExpression('折扣1折\n原价为:' + ['item'].price " +"+ '\n折扣后的价格为:'" +" + T(java.lang.Float).parseFloat(['item'].price * 0.1+'') ) }");rules.register(spelRule);Item item = new Item(500);Facts facts = new Facts();facts.put("item", item);DefaultRulesEngine defaultRulesEngine = new DefaultRulesEngine();// 触发引擎defaultRulesEngine.fire(rules, facts);System.out.println(item.getExpression());}
企业级应用
如果规则引擎的使用仅仅只如以上硬编码的使用方式,还不足以体现规则引擎的强大。试看以下代码:
@Testpublic void factoryTest() throws Exception {ClassPathResource classPathResource = new ClassPathResource("rule.yml");String absolutePath = classPathResource.getAbsolutePath();YamlRuleDefinitionReader ymlReader = new YamlRuleDefinitionReader();MVELRuleFactory mvelRuleFactory = new MVELRuleFactory(ymlReader);Rules rules = mvelRuleFactory.createRules(new FileReader(absolutePath));DefaultRulesEngine rulesEngine = new DefaultRulesEngine();JSONObject entries = new JSONObject();entries.set("productId","1");entries.set("type","1");Facts facts = new Facts();facts.put("canteen", entries);rulesEngine.fire(rules, facts);}
yml内容如下:
---
name: "规则1"
description: "prouductId = 1 && type = 1 "
condition: "canteen.productId==1&&canteen.type==1"
priority: 1
actions:- "com.byd.performance.easyruledemo.four.UserInfoService.getNowTime();"
---
name: "规则2"
description: "prouductId = 1 && type = 2"
condition: "canteen.productId == 2 && canteen.type == 1"
priority: 2
actions:- "System.out.println(2);"
以上通过配置文件定义规则,将规则的触发决策通过文本的方式表述,解耦了业务逻辑代码和规则决策,大大的提升了该引擎的实用性。试想如下业务场景:
公司为了对产品经销商加强管理,需要制定一系列的处罚措施,当触犯某条规则时,将受到相应的惩罚措施。如果采用代码实现决策过程,需要大量的if else来完成,且当需要改变处罚措施时,需要修改代码,重新发布。
如果把决策过程由以上配置文件完成(再配合规则变动刷新等措施或者直接把规则数据由数据库存储加载),可以方便的支持需求变更(此处只是讨论决策的变动,如果需要新增加某种处罚措施也需要代码调整、发布)
源码浅析
easy-rule的源码较为简单(最主要是将触发规则(Condition)和执行逻辑(Action)解耦的思想),以下以简单的示例做源码分析:
org.jeasy.rules.core.DefaultRulesEngine#doFire
可以看出执行引擎的核心逻辑逻辑清晰、简单--就是通过循环Rule,判断是否命中规则,然后执行规则逻辑即可
其中执行引擎有4个参数可设置,分别释义如下:
skipOnFirstAppliedRule:Parameter to skip next applicable rules when a rule is applied(当第一个被命中的规则执行后,是否不执行后续规则)代码line 115就是改功能的释义skipOnFirstNonTriggeredRule:Parameter to skip next applicable rules when a rule is non triggered(当有规则的判断逻辑未命中或判断是否命中异常,是否跳过后续规则,代码line102,line130 是逻辑释义)isSkipOnFirstFailedRule:Parameter to skip next applicable rules when a rule has failed(当执行某个规则逻辑异常时是否跳过后续规则) priorityThreshold:规则优先级的阈值,超过该阈值的规则不被执行,默认值是Integer.MAX_VALUE
功能展望
在上面的企业级应用中有提到过,将规则决策由代码实现转而由配置文件(或者存储于数据库)中实现,解耦了规则和执行逻辑。(执行逻辑--知识库)知识库的更新需要走发布流程。如果实现以下功能是不是能更好的发挥该框架的功能
1 解藕知识库与业务逻辑的耦合性,维护知识库来进行知识与版本的管理
2 动态增加知识库,让JVM动态加载新的知识库
3 屏蔽语言壁垒,能够让运营、产品支持在线规则配置、发布是最终要实现的重要目标。做到无需研发接入,实时发布