规则引擎核心知识与开源产品对比选型


规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策,一个好的规则引擎能大大提高系统的灵活性,扩展性。

规则条件匹配的效率决定了引擎的性能,引擎需要迅速测试工作区中的数据对象,从加载的规则集中发现符合条件的规则,生成规则执行实例。

定义规则

大多数业务规则可以由以下定义表示:

  • 名称:规则命名空间中的唯一规则名称
  • 说明:规则的简要说明
  • 优先级:相对于其他规则的规则优先级
  • 事实:去匹配规则时的一组已知事实
  • 条件:为了匹配该规则,在给定某些事实的情况下应满足的一组条件
  • 动作:当条件满足时要执行的一组动作(可以添加/删除/修改事实)

引擎术语

  • Fact,事实对象,对于真实事物或者事实的承载对象,POJO对象。

  • Rule,规则,由条件构成和结论构成的推理语句。例如:if … then … else …..。多个规则组成规则集。

  • LHS,规则的左半部分,通常指规则的if…. 部分。
  • RHS,规则的右半部分,通常指规则的then以及else部分。一般是指具体的action具体的动作。
  • 知识包,知识包是打包了,某一个业务场景下,所有的规则,所有的库文件(Fact元数据描述库,动作库,常量库,枚举库),甚至包括评分卡,决策流等。一个知识包往往是一个业务场景下大的集合,知识包都有版本概念,可以发布新版本的知识包,当有新的知识包发布,所有依赖某个业务规则的客户端,都会更新为最新知识包下的规则。
  • workmemory,工作区,即执行规则的内存空间,一个workmemory对应一次回话,对应一次规则的执行。
  • Rete,由LHS部分构成的规则网络,通常是dag图。
  • Agenda,议程,决定执行哪些RHS的Action。
  • Action,动作,RHS部分对应的具体动作,例如:赋值,打印参数,执行方法等。

RETE算法

Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年发表的论文中所阐述的算法 , 该算法提供了专家系统的一个高效实现。自 Rete 算法提出以后 , 它就被用到一些大型的规则系统中 , 像 ILog、Jess、JBoss Rules 等都是基于 RETE 算法的规则引擎。

Rete 在拉丁语中译为”net”,即网络。Rete 匹配算法是一种进行大量模式集合和大量对象集合间比较的高效方法,通过网络筛选的方法找出所有匹配各个模式的对象和规则。

其核心思想是将分离的匹配项根据内容动态构造匹配树(其实是构造一个DAG,有向无环图),以达到显著降低计算量的效果。Rete 算法可以被分为两个部分:规则编译和规则执行 。当 Rete 算法进行事实的断言时,包含三个阶段:匹配、选择和执行,称做 match-select-act cycle。本质上是利用空间换换时间,会消耗较多内存。

参考:RETE网络算法

规则引擎对比

目前的规则引擎系统中,使用较多的开源规则引擎是Drools,另外还有商用的规则管理系统BRMS是ILOG JRules。商业的不作分析,就几款有开源版本的做下对比。

Drools:

Drools 是用 Java 语言编写的开放源码规则引擎,基于Apache协议,基于RETE算法,于2005年被JBoss收购。

特性:

  • 简化系统架构,优化应用。
  • 提高系统的可维护性和维护成本。
  • 方便系统的整合。
  • 减少编写“硬代码”业务规则的成本和风险。

Drools发展到今天,其实已经是一整套解决方案了。
如果只是想要简单使用,那就是只用到Drools的核心BRE,引入几个Maven依赖,编写Java代码和规则文件即可。但是如果要编排很复杂的工程,甚至整个业务都重度依赖,需要产品、运营同学一起来指定规则,则需要用到BRMS整套解决方案了,包括Drools Expert(BRE)、Drools Workbench、DMN。
所以我们说Drools太重了,主要是在说:

  • Drools相关的组件太多,需要逐个研究才知道是否需要
  • Drools逻辑复杂,不了解原理,一旦出现问题排查难度高
  • Drools需要编写规则文件,学习成本高
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package rules.testworld

rule rule_001
when
then
System.out.println("执行了无条件规则");
end

rule rule_002
when
$p:Person(name=="张三",birthday=="1987-07-24");
then
$p.setName("李四");
update($p);
System.out.println("执行了 name==\"张三\",birthday==\"1987-07-24\" 规则");
System.out.println("改完后的名字:"+$p.getName());
end

URule

URule是一款纯Java规则引擎,提供基于Apache协议开源版本和商业版本,它以RETE算法为基础,提供了向导式规则集、脚本式规则集、决策表、交叉决策表(PRO版提供)、决策树、评分卡及决策流共六种类型的规则定义方式,配合基于WEB的设计器,可快速实现规则的定义、维护与发布。

仓库地址:https://gitee.com/youseries/urule

URule的优点:

  • 向导式规则配置:可以通过web页面配置规则,不依赖开发人员即可配置规则
  • 决策表、决策树:提供更加友好的、直观的、支持复杂的规则配置
  • 快速测试:在页面可快速构建规则测试
  • 集成方便

缺点:

  • 开源版本提供的功能有限,无高级功能

Easy Rules:

Easy Rules 是一款 Java 规则引擎,它的诞生启发自有Martin Fowler 一篇名为 “Should I use a Rules Engine?” 文章。Easy Rules 提供了规则抽象来创建带有条件和操作的规则,以及运行一组规则来评估条件和执行操作的RulesEngine API。

Easy Rules是一个简单而强大的Java规则引擎,提供以下功能:

  • 轻量级框架和易于学习的API
  • 基于POJO的开发与注解的编程模型
  • 定义抽象的业务规则并轻松应用它们
  • 支持从简单规则创建组合规则的能力
  • 支持使用表达式语言(如MVEL和SpEL)定义规则的能力
1
2
3
4
5
6
7
8
Rule rule = new RuleBuilder()
.name("myRule")
.description("myRuleDescription")
.priority(3)
.when(condition)
.then(action1)
.then(action2)
.build();

官方示例:https://github.com/j-easy/easy-rules/wiki

Aviator:

Aviator是一个高性能、轻量级的java语言实现的表达式求值引擎,主要用于各种表达式的动态求值。现在已经有很多开源可用的java表达式求值引擎,为什么还需要Avaitor呢?

Aviator的设计目标是轻量级高性能 ,相比于Groovy、JRuby的笨重,Aviator非常小,加上依赖包也才450K,不算依赖包的话只有70K;当然,Aviator的语法是受限的,它不是一门完整的语言,而只是语言的一小部分集合。

其次,Aviator的实现思路与其他轻量级的求值器很不相同,其他求值器一般都是通过解释的方式运行,而Aviator则是直接将表达式编译成Java字节码,交给JVM去执行。

支持大部分运算操作符,包括算术操作符、关系运算符、逻辑操作符、位运算符、正则匹配操作符(=~)、三元表达式?: ,并且支持操作符的优先级和括号强制优先级,具体请看后面的操作符列表。

Aviator的限制:

没有if else、do while等语句,没有赋值语句,仅支持逻辑表达式、算术表达式、三元表达式和正则匹配。

不支持八进制数字字面量,仅支持十进制和十六进制数字字面量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.googlecode.aviator.AviatorEvaluator;

import java.util.HashMap;
import java.util.Map;

public class AviatorSimpleExample {
public static void main(String[] args) {
String str = "小哥哥带你使用Aviator";
Map<String,Object> env = new HashMap<>();
env.put("str",str);
Long length = (Long)AviatorEvaluator.execute("string.length(str)",env);
System.out.println(length);
}
}
输出结果 : 14

基于Groovy实现轻量级规则引擎

Groovy是动态语言,依靠反射方式动态执行表达式的求值,并且依靠JIT编译器,在执行次数够多以后,编译成本地字节码,因此性能非常的高,适应于反复执行的表达式,用Groovy脚本动态调整线上代码,无须发版。

Groovy编译器先将.groovy文件编译成.class文件,然后调用JVM执行*.class文件,可以在Java项目中集成Groovy并充分利用Groovy的动态功能;

Groovy兼容几乎所有的java语法,开发者完全可以将groovy当做Java来开发,甚至可以不使用Groovy的特有语法,仅仅通过引入Groovy并使用它的动态能力;

Groovy可以直接调用项目中现有的Java类(通过import导入),通过构造函数构造对象并直接调用其方法并返回结果。

《基于Groovy的规则脚本引擎实战》https://www.codercto.com/a/27512.html

QLExpress

QLExpress从一开始就是从复杂的阿里电商业务系统出发,并且不断完善的脚本语言解析引擎框架,在不追求java语法的完整性的前提下(比如异常处理,foreach循环,lambda表达式,这些都是groovy是强项),定制了很多普遍存在的业务需求解决方案(比如变量解析,spring打通,函数封装,操作符定制,宏替换),同时在高性能、高并发、线程安全等方面也下足了功夫,久经考验。

参考:https://developer.aliyun.com/article/621206


文章作者: KavenRan
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 KavenRan !
 上一篇
分布式架构下全局唯一ID生成方案比较分析 分布式架构下全局唯一ID生成方案比较分析
系统唯一ID是我们在设计一个系统的时候常常会遇见的问题,也常常为这个问题而纠结。生成ID的方法有很多,适应不同的场景、需求以及性能要求。所以有些比较复杂的系统会有多个ID生成的策略。下面就介绍一些常见的ID生成策略。 1. 数据库自增长序列
2021-03-30
下一篇 
平台+标准——数据治理如何落实2021年一号文精神 平台+标准——数据治理如何落实2021年一号文精神
前言 《关于2021年中心全年管理工作部署的通知》明确提出了成本变经营,内部变市场,要素驱动向创新驱动转型。构建组织的确定性,中心要持续建设三个能力,即:以客户为中心的经营能力,以敏捷交付为核心的研发能力,以数字化平台为核心的支撑能力。
2021-01-09
  目录