跟我一起学开源设计第14节: 规则引擎介绍与开源EasyRule基本入门

2,607 阅读18分钟

一、背景

在之前分享的文章中,我介绍了sql-analysis这个京东开源的慢SQL组件源码设计和原理,在分析的过程中,我们知道了它使用了开源的规则引擎EasyRule进行SQL结果的动态分析比较,从而实现了规则的动态维护业务的规则判断。

在介绍之前,我们先来看看这个程序员的故事:

从前有个程序员小李,刚入职一家互联网公司。公司业务复杂,需求变化比女朋友心情还快。一会儿说折扣规则改一下,满100减20;一会儿又说换成买三送一,还要根据用户等级定折扣比例。产品经理小张的需求文档从来没有最复杂,只有更复杂。

​于是小李开始干活了。

第一天:手写逻辑的“狂野生涯”

小李撸起袖子开始写代码,用 if-else 实现折扣规则:

if (userLevel == "VIP") { 
   if (totalAmount > 100) {       
         discount = 20;    
    } else {       
         discount = 10;
    }
} else {  
      discount = 5;
}

小李美滋滋地以为搞定了,没想到第二天产品经理来了句:“再加个新用户首单 50% 折扣规则。”

小李叹了口气,又加了一大堆 if-else。

第一百天:代码成了“噩梦森林”

100天后,小李的折扣逻辑已经长成了一个庞大的“迷宫”:

if (userLevel == "VIP" && isFirstOrder && totalAmount > 100) {    
    // 一堆逻辑
}
 else if (...) {  
  // 又一堆逻辑
}
 else if (...) {  
  // 更复杂的逻辑
}

代码没人敢动,测试小姐姐一改配置就崩,小李的发际线肉眼可见地后移。连产品经理都忍不住说:“你这代码比我大学数学考试的步骤还难懂啊!”

一天,小李发现了“规则引擎”

就在小李绝望的时候,资深架构师老王拍拍他的肩膀:“小伙子,听说过规则引擎吗?”

“规则引擎?这能吃吗?”

老王笑了:“它可比外卖有用得多。规则引擎就像个聪明的裁判,专门处理这种繁琐、动态的业务规则。只需要定义规则条件和动作,改需求的时候直接改规则配置,代码几乎不用动。”

规则引擎拯救世界

小李抱着试一试的心态,接入了一个开源的规则引擎——比如 EasyRules。一个复杂的业务需求变成了简单的规则配置:

- name: "VIP满100减20"
 condition: "user.level == 'VIP' && totalAmount > 100"  
    action: "discount = 20"- name: "新用户首单50%折扣"  
condition: "user.isFirstOrder == true"  
    action: "discount = totalAmount * 0.5"

产品经理惊呼:“改规则不用找你改代码了?!”

测试小姐姐:“规则清晰多了,测试也简单了!”

小李终于可以准时下班了,他的发际线也开始神奇地恢复了。

结局:规则引擎带来的新生活

小李深有感触地总结:“规则引擎,拯救发际线,提升生产力!”

从此,凡是复杂业务逻辑,他都用上了规则引擎,再也没写过乱七八糟的 if-else。

用规则引擎管理复杂规则,就像从“手工种田”迈向了“工业化生产”,它让规则管理更灵活,开发更轻松,团队更和谐。程序员朋友们,再不学规则引擎,你的代码可能就要“变森林”了哦!

​本文开始分享的规则引擎京东慢SQL组件中使用到的规则引擎:EasyRules。

EasyRules 是一个轻量级且易于学习的Java规则引擎,它提供了一个简单的方式来定义业务规则,并在Java应用中轻松应用这些规则,组件设计的灵感来源于马丁·福勒的一篇文章,Easy

Rules旨在帮助开发者以POJO的形式构建规则,并通过条件和动作进行表达。

二、规则引擎知识

首先我来分享下规则引擎的一些知识。

规则引擎描述

规则引擎是一种将业务规则与业务逻辑分离的技术,它可以将复杂的业务规则表示为一组简单的规则,并通过规则引擎来执行这些规则。规则引擎可以提高业务规则的可维护性、可扩展性和可重用性,同时也可以提高业务系统的灵活性和响应能力。

规则引擎的优点

  1. 灵活性:规则引擎可以灵活地定义和修改业务规则,不需要修改业务系统的代码。这种灵活性使得规则引擎能够快速响应业务需求的变化,减少了对业务系统的影响。
  2. 可维护性:规则引擎将业务规则与业务系统分离,便于维护和管理。规则引擎的独立设计使得规则可以独立于业务系统进行维护和更新,降低了业务系统的复杂性。
  3. 高效性:规则引擎可以自动执行规则,减少人工干预,提高业务效率。规则引擎的自动化处理能力能够快速、准确地执行业务规则,提高了业务流程的执行效率。
  4. 可扩展性:规则引擎可以方便地扩展和添加新的规则,适应业务的变化。规则引擎的模块化设计使得规则可以灵活地组合和扩展,满足不同业务场景的需求。
  5. 增强决策能力:规则引擎支持复杂的决策逻辑,能够处理大量的数据和规则。通过使用规则引擎,企业可以提高决策的准确性和效率,更好地应对复杂的业务环境。

规则引擎的应用场景

  1. 金融行业:规则引擎可以用于信用评估、风险管理、欺诈检测等。规则引擎能够根据客户的信用历史、交易行为等数据,快速评估客户的信用等级,识别欺诈行为,为金融机构提供决策支持。
  2. 电信行业:规则引擎可以用于计费、优惠策略、客户忠诚度管理等。规则引擎能够根据客户的通话记录、套餐使用情况等数据,自动计算费用,提供优惠方案,提高客户满意度和忠诚度。
  3. 电商行业:规则引擎可以用于订单处理、促销活动、库存管理等。规则引擎能够根据订单信息、商品库存情况等数据,自动处理订单,执行促销活动,优化库存管理。
  4. 医疗行业:规则引擎可以用于医疗保险理赔、医疗诊断、药品管理等。规则引擎能够根据患者的医疗记录、诊断结果等数据,快速进行理赔审核,提供准确的诊断建议,管理药品库存。

常见的规则引擎的

  1. 典型的风控场景
  2. 活动策略运营
  3. 数据类系统的分析和清洗
  4. 流程分支非常复杂,规则变量庞大;
  5. 有不确定性的需求,变更频率较高;
  6. 需要快速做出响应和决策;
  7. 规则变更期望脱离于开发人员,脱离coding。
  8. 满足XXX条件后执行XXX的内容。
  9. 业务规则管理:用于管理和维护复杂的业务规则,如保险理赔、贷款审批、订单处理等。
  10. 决策支持:用于支持企业的决策过程,如风险评估、信用评级、市场预测等。
  11. 数据验证:用于验证数据的合法性和完整性,如用户注册、订单提交等。

真实场景截图

截图1:这是一个通过在数据库中维护规则引擎表达式的使用场景,通过在数据库中维护规则表达式,从而实现某些Java业务逻辑的动态维护,这个设计是非常有用的:

截图2:这个是通过一个界面来维护规则的关系的规则引擎前端界面,通过在界面中指定多组条件之间的关系,从而形成一个执行的逻辑流程,这种界面和QueryBuilder的东西非常类似,页面的操作也是一定程度上的QueryBuilder的思想。

截图3:这是一个通过界面来维护规则的设计,可以指定每个规则的条件,多个条件形成了条件组,从而形成规则集等等,通过友好的界面拖动实现规则的在线维护等等。

截图4:

这是一个图形化规则引擎系统的维护界面,可以在界面中配置规则引擎的条件与节点关系,实现不同业务场景下的自定义业务流程的规则处理:

通过上面的几个截图,大家可能已经有了一些基本的认识,接下来看看组成部分.

三、规则引擎的组成

3.1、常见术语

先来看张图:

这张图表达式的是,将一些具备特征的数据,输入到规则引擎中,然后再规则引擎中,将特征值/属性作为条件的参数参与条件的执行中,如果条件满足了,则执行结果,多个规则之前通过串行、树结构等等关系实现规则的组合判断。

各项含义如下:

特征:用于作为判断条件的指标、比较值、变量等,特征值:规则配置的变量就是特征值,特征值的数据都来源于数据字典

条件:用于作为规则引擎的的核心判断方式,不同类型有不同的方式

规则:规则引擎里面的最基本的原子定义,支持一系列特征值的基础运算,包括与或运算。隶属于场景,可以被该场景下的所有规则集所引用

规则集:业务调用的基础单位,业务调用使用规则编码,支持多种执行策略,包括全部执行,命中退出,决策流等

决策流:将一系列规则以流的形式组合而成,规则集中的其中一个执行策略

场景:包含多个规则集和多个规则

数据字典:定义一系列类型数据,包括字符,布尔,数值,日期等,作为特征值的原始数据

如图所示:

3.2、常见的几种分类

在市面中,我们通常由如下4类的规则引擎,每类的使用场景稍微有些不同,但是都是符合规则引擎要达到的目标。

概念和区别如下:

1、表达式类规则引擎:核心关注点在于易变逻辑的抽离,通过表达式替换硬编码。

常见的:QLExpress、Aviator

参考资料:

juejin.cn/post/723667…

juejin.cn/post/695943…

2、规则类的规则引擎:是表达式引擎更上一层,核心关注点在于匹配,主要解决规则分散难以维护的问题。

常见的非常成熟的:Drools、urule、JVS-rules、EasyRule

URule Pro是一款由上海锐道信息技术有限公司自主研发的一款纯Java规则引擎,它可以运行在Windows、Linux、Unix等各种类型的操作系统之上;URule Pro的规则设计器采用业内首创的纯浏览器编辑模式,无须安装任何工具,打开浏览器即可完成复杂规则的设计与测试。

URule资料如下:www.bstek.com/resources/d…

3、决策类的规则引擎:是规则引擎更上一层,核心关注点在于判断,主要解决选择的问题。

规则集、决策树、决策矩阵、评分卡等

决策树也称规则树,是由多个规则按一定分支及流程编排而成,直观显示如下:

截图1:

4、流程类的规则引擎: 是表达式引擎或规则引擎之上,核心关注点在于整体的流程刘庄,主要解决流程节点分散管理不直观。

比如开源项目LiteFlow,就是属于这种流程类的规则引擎。

四、开源规则引擎EasyRule的使用

Easy Rules 是一个开源的、轻量级的 Java 规则引擎,它让我们能够用更优雅、清晰的方式定义和管理规则。Easy Rules 支持多种方式定义规则,包括注解、流式 API、表达式语言和 YAML 文件。

easy-rules的Github地址:github.com/j-easy/easy…

接下来我分享如何使用,来源与官方的Github仓库中的ReadME文档。

之前我说道规则引擎是一种能够基于规则自动决策的工具,而大部分我们接触到的规则通常是条件-动作的组合(即“如果...那么...”的逻辑),而规则引擎的任务就是根据输入的事实,检查规则的条件是否成立,然后执行相应的动作。在EasyRule中对相关的概念进行了抽象,所以作为第一次接触到规则引擎的小伙伴,拿EasyRule作为入门学习是不错的起点。

Easy Rules中抽象了几个概念:

Fact(事实):输入的数据,它们用于规则判断,说白了,就是参数。

Rule(规则):由条件和动作组成的业务逻辑,说白了,就是配置,每个配置里面有When逻辑和Then逻辑组成。

根据官方文档介绍,可以通过四种方式来定义规则。

Condition(条件):判断的业务,即When逻辑。

Action(动作):满足条件后的执行动作,即Then逻辑。

方法一:使用注解定义规则

最直接的方式是通过注解来定义规则。我们定义一个简单的天气规则——如果下雨,就带伞

@Rule(name = "天气规则", description = "如果下雨,就带伞")
public class WeatherRule {
    @Condition
    public boolean itRains(@Fact("rain") boolean rain) {
         // 如果 rain 为 true,表示下雨
        return rain;
    }

    @Action
    public void takeAnUmbrella() {
        System.out.println("下雨了,请带伞"); // 如果下雨,打印提示
    }
}

在这个例子中,@Rule 注解用于定义规则名称和描述,@Condition 注解定义了条件,而 @Action 注解定义了满足条件时要执行的动作。

方法二:使用流式 API 定义规则

如果你更喜欢代码驱动的方式,可以通过 Easy Rules 的流式 API 来定义规则:

import org.jeasy.rules.core.RuleBuilder;

Rule weatherRule = new RuleBuilder()
    .name("天气规则")
    .description("如果下雨就带伞")
    .when(facts -> facts.get("rain").equals(true))  // 定义条件:rain 为 true
    .then(facts -> System.out.println("下雨了带一个伞!")) // 定义动作
    .build();

这种方式更适合那些喜欢通过编程方式构建规则的人,所有的规则定义都通过流式调用完成。

方法三:使用表达式语言(MVEL)

使用表达式语言 (MVEL) 可以让规则定义更加简洁。以下是使用 MVEL 语言定义的规则:

import org.jeasy.rules.mvel.MVELRule;

Rule weatherRule = new MVELRule()
    .name("天气规则")
    .description("如果下雨就带伞")
    .when("rain == true")  // 条件为 rain 等于 true
    .then("System.out.println(\"下雨了,带一个伞\");");  // 动作为打印

方法四:使用规则描述符(YAML)

Easy Rules 还支持从外部文件加载规则描述,比如使用 YAML 文件:

# weather-rule.yml
name: "天气规则"
description: "如果下雨了就带伞"
condition: "rain == true"
actions:
  - "System.out.println(\"如果下雨了就带伞!\");"

这种方式非常适合那些需要从配置文件动态加载规则的场景。如果之前理解了上篇文章中的慢SQL组件的设置,相信现在应该又理解了一遍。京东开源慢SQL组件的规则引擎代码如下:

import org.jeasy.rules.mvel.MVELRuleFactory;
import org.jeasy.rules.api.Rule;

MVELRuleFactory ruleFactory = new MVELRuleFactory();
Rule weatherRule = ruleFactory.createRule(new FileReader("weather-rule.yml"));

这种方式非常适合那些需要从配置文件动态加载规则的场景。如果之前理解了上篇文章中的慢SQL组件的设置,相信现在应该又理解了一遍。京东开源慢SQL组件的规则引擎代码如下:

触发规则

在定义了规则之后,我们需要根据实际的事实(即输入数据)来触发规则执行。

import org.jeasy.rules.api.Facts;
import org.jeasy.rules.api.Rules;
import org.jeasy.rules.core.DefaultRulesEngine;

public class Test {
    public static void main(String[] args) {
        // 定义事实
        Facts facts = new Facts();
        facts.put("rain", true);  // 表示当前在下雨

        // 定义规则(可以使用前面任意一种定义方式)
        Rule weatherRule = ...;

        // 注册规则
        Rules rules = new Rules();
        rules.register(weatherRule);

        // 创建规则引擎并触发规则
        DefaultRulesEngine rulesEngine = new DefaultRulesEngine();
        rulesEngine.fire(rules, facts);  // 根据事实触发规则
    }
}

在这个例子中,我们首先创建一个 Facts 对象,存储当前的输入数据(在这个例子里,rain 为 true,表示下雨),然后创建并注册规则,最后通过规则引擎根据事实执行满足条件的规则。规则成功地匹配了当前的事实,并执行了相应的动作。

通过这个例子,我们已经成功创建并触发了一个简单的规则。如果你需要处理更复杂的逻辑,Easy Rules 还提供了更多强大的功能,比如规则优先级、规则群组、条件组合等。

Easy Rules 提供了多种方式来定义和使用规则,无论是注解、流式 API、表达式语言还是外部配置文件,都能满足不同场景的需求。你可以根据你的项目需求选择最适合的方式。

在实际开发中,规则引擎可以帮助我们将复杂的业务逻辑进行解耦,提升代码的可读性和维护性。

希望通过本文的介绍,你已经对 Easy Rules 有了一个基本的了解,并能够在自己的项目中尝试使用它。

在这个项目的仓库中的代码,也提供了相应的例子:

在这里面我们经常使用到的注解如下:

@Rule注解: 规则的声明,作用于类上,说明是一个规则

@Condition注解:规则条件声明,作用于Rule中的一个方法上,返回值为布尔类型。

@Action注解:规则方法注解,当满足一个Rule条件时需要执行的方法,作用于方法。order属性满足条件时有多个方法,方法的执行顺序。值越小越先执行。

@Fact注解:参数注解,可作用于Condition和Rule的方法参数上。

@Priority注解:Rule优先级注解,作用在一个方法上,该方法返回integer类型的值作为rule的优先级。

为了能够让大家更好的理解,我用AI生成了一下EasyRule的前端可视化配置界面,效果如下所示:

可以在界面上配置规则基本信息,然后单击生成代码,可以生成代码样例:

我然后又让AI给我转换为Vue.js格式的代码,也是非常不错的:

AI还建议我生成如下功能:

  1. 条件组扩展:支持不同的逻辑组合(如 "或者" 组合条件)。
  2. 复杂条件支持:可以让用户在生成的条件表达式中插入括号,形成更复杂的逻辑表达式。
  3. 动作配置扩展:可以支持多种动作(如多行代码)或者条件触发不同动作。
  4. 保存配置:可以添加保存配置的功能,将用户配置的条件组和规则保存为文件或者发送到后端进行处理。

真的说,AI真的太强大了,程序员赶紧被淘汰吧。

这个之前写过了一篇文章进行分享:

juejin.cn/post/741437…

五、总结

本文分享了关于规则引擎的业务知识、规则引擎的分类、EasyRule开源规则引擎的介绍,希望大家可以学会使用这个规则引擎,从而具备如何设计一种动态化的程序判断、一种基于规则的程序判断,从而设计出更佳解耦合的程序设计。