Easy Rules规则引擎(1-基础篇)

目录

一、序言

最近团队在做一些VisaMaster卡的交易风控,运营团队提供了一些交易风控的规则,比如针对卡号MCC设置单笔交易限额,24小时交易限额,72小时交易限额等等,还有触发风控规则是否拦截交易还是只发告警邮件等等等。

虽然写各种条件判断也能实现,但是随着后面规则增加,维护成本也会越来越高,所以想尝试引入规则引擎,同时考虑到开发和学习成本,还是决定学习轻量级的Easy Rules


二、Easy Rules介绍

Easy Rules是一个Java规则引擎,它提供了规则抽象,通过触发条件和触发后的行为去创建规则。还提供了规则引擎API,通过这些API可以基于一系列的规则去判断规则是否触发,以及触发后执行什么动作。

核心特性:

  • 轻量级Java库,易于学习的API。
  • 注解式编程模型实现基于POJO开发。
  • 通过抽象定义业务规则并且轻松应用规则。
  • 支持通过简单规则可以创建组合规则。
  • 支持通过表达式语言(MVEL、SPEL和JEXL)定义规则。

相关依赖如下:

java 复制代码
<!--Easy Rule-->
<!--核心库-->
 <dependency>
     <groupId>org.jeasy</groupId>
     <artifactId>easy-rules-core</artifactId>
     <version>4.1.0</version>
 </dependency>
 <!--组合规则支持-->
 <dependency>
     <groupId>org.jeasy</groupId>
     <artifactId>easy-rules-support</artifactId>
     <version>4.1.0</version>
 </dependency>
 <!--SPEL表达式语言支持-->
 <dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-spel</artifactId>
    <version>4.1.0</version>
</dependency>
 <!--MVEL表达式语言支持-->
<dependency>
    <groupId>org.jeasy</groupId>
    <artifactId>easy-rules-mvel</artifactId>
    <version>4.1.0</version>
</dependency>

三、定义规则(Rules)

1、规则介绍

大多数的业务规则可以通过如下定义来描述:

  • Name:唯一的规则名称。
  • Description:简单规则描述。
  • Priority:规则执行优先级。
  • Facts:触发规则时的一系列事实。
  • Condition:给定事实后,应该被满足的一系列条件。
  • Actions:条件满足时应该执行的一系列行为。

Easy Rules中的规则由Rule接口来代表,如下:

java 复制代码
public interface Rule extends Comparable<Rule> {

    /**
    * 判断规则是否应该被触发,true-是,false-否
    */
    boolean evaluate(Facts facts);

    /**
    * 规则触发后执行的行为
    * @throws Exception 执行时触发的异常
    */
    void execute(Facts facts) throws Exception;
}

2、编程式规则定义

java 复制代码
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;

/**
 * 编程式规则定义
 * @author Nick Liu
 * @date 2023/8/3
 */
public class ProgrammaticHelloWorldRule implements Rule {

	@Override
	public boolean evaluate(Facts facts) {
		return facts.get("enabled");
	}

	@Override
	public void execute(Facts facts) throws Exception {
		System.out.println("Hello World");
	}

	@Override
	public int compareTo(Rule o) {
		return 0;
	}

	public static void main(String[] args) {
		// 定义事实
		Facts facts = new Facts();
		facts.put("enabled", true);

		// 注册编程式规则
		Rules rules = new Rules();
		rules.register(new ProgrammaticHelloWorldRule());

		// 使用默认规则引擎根据事实触发规则
		RulesEngine rulesEngine = new DefaultRulesEngine();
		rulesEngine.fire(rules, facts);
	}
}

备注:运行程序控制台会输出Hello World

3、声明式规则定义

java 复制代码
import org.jeasy.rules.annotation.Action;
import org.jeasy.rules.annotation.Condition;
import org.jeasy.rules.annotation.Fact;
import org.jeasy.rules.annotation.Rule;
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.DefaultRulesEngine;

/**
 * 声明式规则定义
 * @author Nick Liu
 * @date 2023/8/3
 */
@Rule(name = "Hello world rule", description = "Always say hello world")
public class DeclarativeHelloWorldRule {

	@Condition
	public boolean when(@Fact("enabled") boolean enabled) {
		return enabled;
	}

	@Action(order = 1)
	public void then(@Fact("enabled") boolean enabled) throws Exception {
		System.out.println("Hello World");
	}

	@Action(order = 2)
	public void finalAction(Facts facts) throws Exception {
		System.out.println("Final Hello World");
	}

	public static void main(String[] args) {
		Facts facts = new Facts();
		facts.put("enabled", true);

		Rules rules = new Rules();
		rules.register(new DeclarativeHelloWorldRule());

		RulesEngine rulesEngine = new DefaultRulesEngine();
		rulesEngine.fire(rules, facts);
	}
}

控制台运行结果如下:

c 复制代码
Hello World
Final Hello World

四、定义事实(Facts)

在Easy Rules中,事实由Fact类来定义,如下:

java 复制代码
public class Fact<T> {
   private final String name;
   private final T value;
}

事实有namevalue两个属性,两者都不能为空,且name属性值充当命名空间的角色需要唯一。

下面是定义事实的例子:

  • 第1种方式
java 复制代码
Fact<String> fact = new Fact("foo", "bar");
Facts facts = new Facts();
facts.add(fact);
  • 第2种方式
java 复制代码
Facts facts = new Facts();
facts.put("foo", "bar");

备注:两者方式都定义了一个namefoovaluebar的事实实例,第二种方式更加简洁。


五、定义规则引擎(Rules Engine)

1、规则引擎介绍

Easy Rules提供了两种规则引擎的实现:

  • DefaultRulesEngine:默认规则引擎,根据规则的自然顺序(默认为优先级)应用规则。
  • InferenceRulesEngine:推理规则引擎,持续性应用单条规则,直到规则触发条件不满足。

Easy Rules规则引擎支持下面参数配置:

参数名称 参数类型 必选 默认值
rulePriorityThreshold int Integer.MAX_VALUE
skipOnFirstAppliedRule boolean false
skipOnFirstFailedRule boolean false
skipOnFirstNonTriggeredRule boolean false
  • skipOnFirstAppliedRule: 当规则被触发并且成功执行行为后是否跳过下条规则。
  • skipOnFirstFailedRule : 当判断规则是否触发抛出异常或者触发成功但行为执行后抛出异常是否跳过下条规则。
  • skipOnFirstNonTriggeredRule : 当规则未被触发是否跳过下条规则。
  • rulePriorityThreshold : 如果规则优先级超过默认阈值,则跳过下条规则。

参数配置示例如下:

java 复制代码
RulesEngineParameters parameters = new RulesEngineParameters()
    .rulePriorityThreshold(10)
    .skipOnFirstAppliedRule(true)
    .skipOnFirstFailedRule(true)
    .skipOnFirstNonTriggeredRule(true);

RulesEngine rulesEngine = new DefaultRulesEngine(parameters);

通过下面的代码可以获取规则引擎参数:

java 复制代码
RulesEngineParameters parameters = myEngine.getParameters();

2、InferenceRulesEngine规则引擎示例

DefaultRulesEngine默认规则引擎的使用示例前面已经有提到过,下面我们看下InferenceRulesEngine推理规则引擎的代码示例。

(1) 定义触发条件

java 复制代码
import org.jeasy.rules.api.Condition;
import org.jeasy.rules.api.Facts;

/**
 * @author Nick Liu
 * @date 2023/8/5
 */
public class HighTemperatureCondition implements Condition {

	@Override
	public boolean evaluate(Facts facts) {
		int temperature = facts.get("temperature");
		return temperature > 25;
	}
}

(2) 定义规则触发后的执行行为

java 复制代码
import org.jeasy.rules.api.Action;
import org.jeasy.rules.api.Facts;

/**
 * @author Nick Liu
 * @date 2023/8/5
 */
public class DecreaseTemperatureAction implements Action {

	@Override
	public void execute(Facts facts) throws Exception {
		int temperature = facts.get("temperature");
		System.out.printf("Current temperature: %d, It's hot! cooling air...%n", temperature);
		facts.put("temperature", temperature - 1);
	}
}

(3) 测试用例

java 复制代码
import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rule;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.api.RulesEngine;
import org.jeasy.rules.core.InferenceRulesEngine;
import org.jeasy.rules.core.RuleBuilder;

/**
 * @author Nick Liu
 * @date 2023/8/5
 */
public class AirConditionLauncher {

	public static void main(String[] args) {
		Facts facts = new Facts();
		facts.put("temperature", 30);

		// 通过规则构建API定义规则
		Rule rule = new RuleBuilder()
			.name("Air Condition Rule")
			.when(new HighTemperatureCondition())
			.then(new DecreaseTemperatureAction())
			.build();
		Rules rules = new Rules();
		rules.register(rule);

		// 基于事实重复应用规则的推理规则引擎,直到规则不再满足
		RulesEngine rulesEngine = new InferenceRulesEngine();
		rulesEngine.fire(rules, facts);
	}
}

控制台输出结果如下:

bash 复制代码
Current temperature: 30, It's hot! cooling air...
Current temperature: 29, It's hot! cooling air...
Current temperature: 28, It's hot! cooling air...
Current temperature: 27, It's hot! cooling air...
Current temperature: 26, It's hot! cooling air...

备注:可以看到定义的规则会持续触发,直到temperature的值为25。

相关推荐
阿伟*rui31 分钟前
配置管理,雪崩问题分析,sentinel的使用
java·spring boot·sentinel
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
paopaokaka_luck2 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
dayouziei2 小时前
java的类加载机制的学习
java·学习
Yaml44 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~4 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616884 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
aloha_7895 小时前
从零记录搭建一个干净的mybatis环境
java·笔记·spring·spring cloud·maven·mybatis·springboot
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
睡觉谁叫~~~6 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust