【中间件】一文搞懂规则引擎 - Easy Rules

【规则引擎】
规则引擎介绍
规则引擎全称为业务规则管理系统,英⽂名为 BRMS(即 Business Rule Management System)。规则引擎的主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。 需要注意的是规则引擎并不是一个具体的技术框架,而是指的⼀类系统,即业务规则管理系统。 规则引擎实现了将业务决策从应用程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。规则引擎其实就是一个输入输出的平台。
规则引擎就是提供一种可选的计算模型。与通常的命令式模型(由带有条件和循环的命令依次组成)不同,规则引擎基于生产规则系统。这是一组生产规则,每条规则都有一个条件(condition)和一个动作(action)。简单地说,可以将其看作是一组 if-then 语句。目前市面上具体的规则引擎产品有:Drools、VisualRules、iLog 等,使用最为广泛并且开源的是 Drools。
规则引擎解决什么问题?
硬编码实现业务规则难以维护硬编码实现业务规则难以应对变化业务规则发⽣变化需要修改代码,重启服务后才能生效
规则引擎优势
业务规则与系统代码分离,实现业务规则的集中管理在不重启服务的情况下可随时对业务规则进行扩展和维护可以动态修改业务规则,从⽽快速响应需求变更规则引擎是相对独立的,只关心业务规则,使得业务分析⼈员也可以参与编辑、维护系统的业务规则减少了硬编码业务规则的成本和风险使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单
【Easy Rules】
Easy Rules 介绍
Easy Rules 是一个 Java 规则引擎。Easy Rules 它提供 Rule 抽象以创建具有条件和动作的规则,并提供 RuleEngine API,该 API 通过一组规则运行以评估条件并执行动作。
Easy Rules 概念
规则(Rule): 规则是 EasyRules 框架中的核心概念,它用于描述应用程序中需要遵循的规则。每个规则通常包含两个部分:规则名称和规则条件。规则条件(Condition): 规则条件定义了规则的前提条件。如果规则条件为 true,则规则将被触发执行。否则,规则将被忽略。规则动作(Action): 规则动作是在规则被触发时执行的一段代码。它可以用于实现各种应用程序逻辑,例如更新数据、发送消息等。规则执行(Rule Engine): 规则执行是 EasyRules 框架的核心功能之一,它负责解析规则条件,并根据条件执行相应的规则动作。
【Easy Rules 入门案例】
SpringBoot 集成 Easy Rules
# ------------------------------------ 配置规则 ---------------------------------------
---
name: "无效加班"
description: "加班费的结算"
priority: 1
condition: "time<=80"
actions:
- "reason.append('加班少于80小时,不发加班费;');"
---
name: "有效加班"
description: "加班费的结算"
priority: 2
condition: "time>80"
actions:
- "money=(money+10*(time-80));reason.append('加班费:').append(10*(time-80)).append(';');"
---
name: "迟到警告"
description: "迟到的惩罚"
priority: 1
condition: "count<=3"
actions:
- "reason.append('迟到小于3次,暂时不扣钱;');"
---
name: "迟到扣钱"
description: "迟到的惩罚"
priority: 2
condition: "count>3"
actions:
- "money=(money - (count-3)*1000);reason.append('迟到大于3次,扣钱:').append((count - 3) * 1000).append(';');"
// ------------------------------------ 测试代码 ---------------------------------------
@GetMapping("/test")
public String test() throws Exception {
// 配置规则引擎参数
RulesEngineParameters parameters = new RulesEngineParameters().skipOnFirstAppliedRule(false);
// 初始化规则引擎
RulesEngine engine = new DefaultRulesEngine(parameters);
// 规则文件读取(通过配置文件方式配置规则)
Rules rules = new MVELRuleFactory(new YamlRuleDefinitionReader()).createRules(new BufferedReader(new InputStreamReader(Objects.requireNonNull(this.getClass().getClassLoader().getResourceAsStream("rules.yml")))));
// 创建事实对象
Facts facts = new Facts();
facts.put("money", 1000);
facts.put("time", 900);
facts.put("count", 5);
facts.put("reason", new StringBuffer());
// 根据事实对象与规则,通过规则引擎进行执行
engine.fire(rules, facts);
// 获取结果:加班费:8200;迟到大于3次,扣钱:2000;
String reason = facts.get("reason").toString();
return reason;
}
【Easy Rules 定义规则方式】
方式一:注解
@Rule(name = "weather rule", description = "if it rains then take an umbrella")
public class WeatherRule {
@Condition
public boolean itRains(@Fact("rain") boolean rain) {
return rain;
}
@Action(order = 1)
public void takeAnUmbrella() {
System.out.println("It rains, take an umbrella!");
}
}
方式二:链式编程
Rule weatherRule = new RuleBuilder()
.name("weather rule")
.description("if it rains then take an umbrella")
.when(facts -> facts.get("rain").equals(true))
.then(facts -> System.out.println("It rains, take an umbrella!"))
.build();
方式三:表达式
Rule weatherRule = new MVELRule()
.name("weather rule")
.description("if it rains then take an umbrella")
.when("rain == true")
.then("System.out.println(\"It rains, take an umbrella!\");");
方式四:yml 配置文件
name: "weather rule"
description: "if it rains then take an umbrella"
condition: "rain == true"
actions:
- 'System.out.println("It rains, take an umbrella!");'
MVELRuleFactory ruleFactory = new MVELRuleFactory(new YamlRuleDefinitionReader());
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));
方式五:json 文件
[
{
"name": "Weather Rule",
"description": "If it rains, take an umbrella",
"priority": 1,
"condition": "facts['weather'] == 'rainy'",
"actions": ["System.out.println('It rains, take an umbrella!')"]
}
]
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.core.DefaultRulesEngine;
import org.jeasy.rules.mvel.MVELRule;
import org.jeasy.rules.support.reader.JsonRuleDefinitionReader;
import org.jeasy.rules.support.reader.RuleDefinitionReader;
import java.io.FileReader;
import java.util.List;
public class DynamicRuleExample {
public static void main(String[] args) throws Exception {
// 读取 JSON 文件
RuleDefinitionReader reader = new JsonRuleDefinitionReader();
List
// 定义事实
Facts facts = new Facts();
facts.put("weather", "rainy");
// 加载规则并执行
DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
rulesEngine.fire(new org.jeasy.rules.api.Rules(rules), facts);
}
}
【Easy Rules 详解】
引擎配置参数
在配置规则引擎初始化参数的时候可以设置引擎的其他参数。
rulePriorityThreshold: 整型,当优先级超过指定的阈值时,跳过余下的规则。skipOnFirstAppliedRule: 布尔类型,当一个规则成功应用时,跳过余下的规则。skipOnFirstFailedRule: 布尔类型,当一个规则失败时,跳过余下的规则。skipOnFirstNonTriggeredRule: 布尔类型,当一个规则未触发时,跳过余下的规则。
// 配置规则引擎参数
RulesEngineParameters parameters = new RulesEngineParameters();
parameters.priorityThreshold(10);
parameters.setSkipOnFirstAppliedRule(true);
parameters.setSkipOnFirstFailedRule(true);
parameters.setSkipOnFirstNonTriggeredRule(true);
规则定义参数
无论以那种方式进行代码的编写,最终实现的都是需要定义如下几个参数,通过不同的参数对规则进行描述。
Name: 一个命名空间下的唯一的规则名称【对应代码中的 then() 与 @Rule 中 name 属性】Description: 规则的简要描述(非必填)【对应代码中的 then() 与 @Rule 中 description 属性】Priority: 相对于其他规则的优先级,默认优先级为 Integer.MAX_VALUE,值越小,优先级越高。【对应代码中的@Actions 中 order 属性】Conditions: 为了应用规则而必须满足的一组条件【对应代码中的 when() 与 @Condition 】Actions: 当条件满足时执行的一组动作【对应代码中的 then() 与 @Actions 】Facts: 事实,可理解为要处理的数据。
【Easy Rules 监听器】
Easy Rules 监听器
在 Easy Rules 中,有 RulesEngineListener 和 RuleListener 两种不同粒度的监听器,用于监控规则引擎的运行过程。它们的核心区别在于监听范围和触发时机
注入监听器
在创建规则引擎之后可以通过对应的属性注入 RuleListener 和 RulesEngineListener。
// 配置规则引擎参数
RulesEngineParameters parameters = new RulesEngineParameters();
// 初始化规则引擎
DefaultRulesEngine engine = new DefaultRulesEngine(parameters);
// 创建自定义的RuleListener
IMoMeiRuleListener iMoMeiRuleListener = new IMoMeiRuleListener();
// 创建自定义的RulesEngineListener
IMoMeiRulesEngineListener iMoMeiRulesEngineListener = new IMoMeiRulesEngineListener();
// 配置RuleListener
engine.registerRuleListeners(List.of(iMoMeiRuleListener));
// 配置RulesEngineListener
engine.registerRulesEngineListeners(List.of(iMoMeiRulesEngineListener));
RuleListener 规则监听器
作用对象: 单个规则的执行过程主要用途: 监听单个规则的评估(evaluate)和执行(execute)生命周期常用场景:
记录单个规则的执行耗时处理规则执行时的异常根据条件跳过特定规则的执行统计规则命中率
// ---------------------------------- 规则监听器接口 -----------------------------------
public interface RuleListener {
/**
* 规则匹配前(所有场景都会进)
*
* @param rule 规则
* @param facts 事实
* @return
*/
default boolean beforeEvaluate(Rule rule, Facts facts) {
return true;
}
/**
* 规则匹配后(所有场景都会进)
*
* @param rule 规则
* @param facts 事实
* @param evaluationResult 匹配结果
*/
default void afterEvaluate(Rule rule, Facts facts, boolean evaluationResult) {
}
/**
* 规则匹配异常
*
* @param rule 规则
* @param facts 事实
* @param exception 异常信息
*/
default void onEvaluationError(Rule rule, Facts facts, Exception exception) {
}
/**
* 规则执行前(需要规则匹配)
*
* @param rule 规则
* @param facts 事实
*/
default void beforeExecute(Rule rule, Facts facts) {
}
/**
* 规则执行成功
*
* @param rule 规则
* @param facts 事实
*/
default void onSuccess(Rule rule, Facts facts) {
}
/**
* 规则执行失败
*
* @param rule 规则
* @param facts 事实
* @param exception 异常信息
*/
default void onFailure(Rule rule, Facts facts, Exception exception) {
}
}
RulesEngineListener 引擎监听器
作用对象: 整个规则引擎的执行流程主要用途: 监听整个引擎的执行过程(例如所有规则的检查、执行顺序)常用场景:
记录引擎整体执行时间监控规则执行的整体成功率在引擎执行前后进行资源初始化和清理控制规则执行流程(如提前终止)
// ---------------------------------- 引擎监听器接口 -----------------------------------
public interface RulesEngineListener {
/**
* 在执行规则集之前触发
*
* @param rules 要触发的规则集
* @param facts 触发规则前的事实
*/
default void beforeEvaluate(Rules rules, Facts facts) { }
/**
* 在执行规则集之后触发
*
* @param rules 要触发的规则集
* @param facts 触发规则前的事实
*/
default void afterExecute(Rules rules, Facts facts) { }
}
【Easy Rules 高级用法】PS:后续补充
规则链
正向链式触发
反向链式触发
异步规则执行
与 Spring Boot 集成
动态规则加载与热更新