最近,项目上遇到了规则引擎的使用场景,我们通过规则引擎实现接口的可配置编排,将接口编排从代码中抽离出来,通过界面拖拽配置实现,还可以借助 Groovy 实现接口出入参的动态调整,本篇文章我就一起来了解一下规则引擎,主要介绍 Drools 和 LiteFlow 两个开源规则引擎。
1. 前言
规则引擎和流程引擎毫不相干,完全是两个场景应用的,规则引擎处理的是易变逻辑和业务耦合问题,流程引擎处理的是业务在不同角色间的流转,大家注意不要混淆了。
个人感觉开源的规则引擎 Drools 和 LiteFlow 的市场覆盖率会更高一些,本文就以这两者为例进行较为深入的介绍,并比较其特性,让大家可以选择适合自己项目的规则引擎。
2. 规则引擎概念
规则引擎全称为业务规则管理系统,英文名为 BRMS(Business Rule Management System)。规则引擎主要思想是将应用程序中的业务决策部分分离出来,并使用预定义的语义模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理。需要注意的是规则引擎并不是一个具体的技术框架,而是指一类系统,及业务规则管理系统。目前市面上具体的规则管理引擎产品有:Drools、LiteFlow、Aviator、QLExpress、 EasyRules、URule 等,使用最为广泛并且开源的是 Drools。规则引擎实现了将业务决策从应用程序代码中分离出来,接收数据输入,解释业务规则,并根据业务规则做出业务决策。规则引擎其实就是一个输入输出平台。
3. 规则引擎应用场景
对于一些存在比较复杂业务规则且会频繁变动的系统比较适合使用规则引擎,如下:
风控平台------风险贷款、风险评估 反欺诈项目----银行贷款、征信验证 保险理赔------保险条款制定理赔规则 决策平台------财务计算 促销平台------满减、打折、加价 编排平台------接口编排
4. 规则引擎特点
业务与代码分离:规则引擎将业务规则与应用程序代码分离,使得业务规则的修改无需修改代码,降低了维护成本。 灵活性:支持动态修改规则,以适应业务变化的需求。 可重用性:规则可以跨多个应用程序和场景重复使用,提高开发效率。 可视化设计:许多规则引擎提供了可视化设计器,允许非技术人员通过图形化界面来定义和修改规则。
5. 规则引擎 Drools 与 LiteFlow 对比
5.1. 社区
Drools 成名比较早,而 LiteFlow 是近几年才开源的一款规则引擎,对比而言我更喜欢 LiteFlow,经过几年的迭代,功能也逐步完善,而且是国内的开源项目,文档更加清晰完善,也有活跃的社区。
5.2. 设计理念
Drools 侧重于将易变逻辑提取,抽象成一个规则文件,通过热变更的方式进行应用,LiteFlow 是侧重组件化,它可以设计各种规则组件,同时支持业务逻辑脚本化,通过对一系列的组件进行编排,形成一个编排文件,然后通过热变更生效。LiteFlow 将很多逻辑抽离到组件里,编排文件会更加简洁易读,更加低耦合,组件也可以实现复用。
5.3. 规则配置
Drools 通过 RETE 算法来实现规则配置,LiteFlow 通过 EL 表达式来实现规则配置,他们二者基本都可以实现同步,异步,选择,条件,循环等功能。
5.4. 数据交换
Drools 可以通过 import 进入 Java 包进行调用,LiteFlow 则通过脚本组件,使用 Groovy 引入包调用,Drools 可以直接引用 fact 对象,LiteFlow 可以直接使用上下文对象,LiteFlow 支持@ScriptBean 注解,将 Spring 上下文中的 Bean 引入直接使用,可以在脚本中直接进行 RPC 调用或者调用 DAO 操作数据库。
5.5. API 调用
Drools 的 Api 更为复杂,需要获取 KieServices、KieContainer,KieSession 等一系列对象,而 LiteFlow 只需要使用到 LiteFlowExecutor 对象。此外 LiteFlow 与 SpringBoot 的集成也更为方便,直接通过 starer 接入。
5.6. 规则存储
两者的规则均支持存储在任意位置,但是 Drools 需要手动去实现这些规则,而 LiteFlow 框架支持了标准的 SQL 数据库,还支持了 Nacos,Etcd,Zookeeper 等注册中心,也支持自己扩展。
5.7. 热刷新机制
Drools 的热刷新机制是通过生成 jar 的方式,远程动态读取 jar 来完成,这种方式已经比较落后了,而 LiteFlow 就现代很多,通过注册中心存储的,自动支持热刷新,通过数据库存储的可以改变规则后手动调用 API 来实现。
5.8. 界面支持
Drools 有 workbrench,workbrench 是一个 war 包,可以直接部署在 tomcat 上,它提供了 web 界面支持编写规则和 fact 对象,提供了检查和部署规则的能力。它并没有实现拖拽实现规则编排的能力,这点其实还是挺重要的,这取决于我们能不能将规则开放给开发者之外的角色。LiteFlow 并未有此方面的支持,只能借助配置中心提供的能力实现,或者自己开发界面。在 LiteFlow 的官方文档上,也看到了作者有开发界面支持的打算,如果这个能落地,感觉 LiteFlow 的可用场景会大大增加,能够开放给开发人员以外的人去用,价值很大。
5.9. 性能
如果 LiteFlow 完全依赖于脚本组件的执行,性能上比 Drools 差很多,但是如果将 LiteFlow 使用 Java 组件执性能就会好于 Drools,在实际的场景中,LiteFlow 是将 Java 组件与脚本组件结合的方式使用,脚本组件能带来更高的灵活性。
5.10. 总结
对比 Drools 和 LiteFlow,个人更倾向于后来者 LiteFlow,感觉它的设计优于 Drools,而且更加易用,对于 Spring 整个生态支持也更友好,此外它的中文文档看着也更舒服一些,期待它可以支持界面拖拽配置规则。
6. 规则引擎 Drools
6.1. 介绍
Drools 是一款由 JBoss 组织提供的基于 Java 语言开发的开源规则引擎,可以将复杂的业务规则从硬编码解耦出来,以规则脚本的形式存放在文件或者特定的存储介质中(如数据库)使得业务规则的变更不需要修改项目代码,不用重启服务器就可以在线上环境立即生效。
6.2. 案例
首先添加依赖
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.63.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-mvel</artifactId>
<version>7.63.0.Final</version>
</dependency>
在 Resource 下 META-INF 中添加 kmodule.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
<kbase name="rules" packages="rules" default="true">
<ksession name="defaultKieSession" default="true"/>
</kbase>
</kmodule>
创建 Fact 对象,Fact 对象指的是将一个普通 JavaBean 插入到规则 WorkingMemory 中后的对象。Drools 可以对 Fact 对象进行任意的读写操作,当一个 JavaBean 插入到 WorkingMemory 当中变成 Fact 之后,Fact 对象不是对原来的 JavaBean 对象进行 Clone,而是原来 JavaBean 对象的引用。
package com.example.droolsdemo.entity;
public class Order {
private int price;
private int qty;
private int score;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public int getQty() {
return qty;
}
public void setQty(int qty) {
this.qty = qty;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Order{" +
"price=" + price +
", qty=" + qty +
", score=" + score +
'}';
}
}
在 Drools 当中,一个标准的规则文件就是一个以“.drl”结尾的文本文件,由于它是一个标准的文本文件,所以可以通过一些记事本工具对其进行打开、查看和编辑。通过对 idea 增加插件可以使我们在编写规则文件时,语法高亮。
在 packages 指定的路径添加规则文件,如上文需要在 Resource 目录下新建 rules 文件夹将规则文件写入该目录
下面案例是根据订单金额添加对应积分,规则如下:
package rules; import com.example.droolsdemo.entity.Order; // 100元以下不积分 rule "score_1" when $order:Order(price * qty < 100) then $order.setScore(0); System.out.println("100元以下不积分"); end // 100-500元 积100分 rule "score_2" when $order:Order((price * qty >= 100) && (price * qty < 500)) then $order.setScore(100); System.out.println("100-500元 积100分"); end // 500-1000元 积500分 rule "score_3" when $order:Order((price * qty >= 500) && (price * qty < 1000)) then $order.setScore(500); System.out.println("500-1000元 积500分"); end // 大于1000元 积1000分 rule "score_4" when $order:Order(price * qty >= 1000) then $order.setScore(1000); System.out.println("大于1000元 积1000分"); end
新建测试用例,如下所示
我们需要先获取 KieServices 服务,再通过它获取容器,然后用过容器获取 session,insert 的作用就是将 order 对象插入 Working Memory 使其称为一个 Fact 对象,可以通过规则引擎访问和操作。
注意:一旦调用 insert 函数,那么 Drools 会重新与所有的规则再重新匹配一次,对于没有设置 no-loop 属性为 true 的规则(不会重复调用),如果条件满足,不管其之前是否执行过都会再执行一次,这个特性不仅存在于 insert 函数上,同时 update、retract 函数同样具有该特性,所以在某些情况下因考虑不周调用 insert、update 或 retract 容易发生死循环。
public class TestDrools { public static void main(String[] args) { // 获取服务 KieServices kieServices = KieServices.Factory.get(); // 获取容器 KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer(); // 获取Session KieSession kieSession = kieClasspathContainer.newKieSession(); Order order = new Order(); order.setPrice(320); order.setQty(2); // 将数据交给规则引擎,规则引擎会根据数据进行规则匹配 kieSession.insert(order); // 执行规则引擎 kieSession.fireAllRules(); // 关闭规则引擎 kieSession.dispose(); System.out.println("执行规则后积分:"+order.getScore()); } }
目录情况如下图
7. 规则引擎 LiteFlow
7.1. 介绍
在每个公司的系统中,都有一些拥有复杂业务逻辑的系统,这些系统承载着核心业务逻辑,几乎每个需求都和这些核心业务有关,这些核心业务业务逻辑冗长,涉及内部逻辑运算,缓存操作,持久化操作,外部资源调取,内部其他系统 RPC 调用等等。时间一长,项目几经易手,维护成本就会越来越高。各种硬代码判断,分支条件越来越多。代码的抽象,复用率也越来越低,各个模块之间的耦合度很高。一小段逻辑的变动,会影响到其他模块,需要进行完整回归测试来验证。如要灵活改变业务流程的顺序,则要进行代码大改动进行抽象,重新写方法。实时热变更业务流程,几乎很难实现。
LiteFlow 就是解决上述问题的存在,它可以实现业务逻辑的解耦,为编排而生,可以灵活调整流程顺序。
如下图所示,瀑布模式的代码很难管理,很难灵活调整顺序执行规则,LiteFlow 就可以灵活的帮助我们进行调整。
下图是对核心流程的替换修改,只需要改编排规则。
此外 LiteFlow 支持众多脚本组件,可以非常灵活。
7.2. 案例
在 SpringBoot 环境引入 Jar 包
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>2.12.4.1</version>
</dependency>
增加 Java 组件,新建三个类 ACmp,BCmp,CCmp
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
//do your business
}
}
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
//do your business
}
}
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
//do your business
}
}
配置文件中指定编排文件位置
liteflow.rule-source=config/flow.el.xml
在 Resources 下的 config/flow.el.xml 中编写编排规则
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN(a, b, c);
</chain>
</flow>
在启动类声明扫描包
@SpringBootApplication //把你定义的组件扫入Spring上下文中 @ComponentScan({"com.xxx.xxx.cmp"}) public class LiteflowExampleApplication { public static void main(String[] args) { SpringApplication.run(LiteflowExampleApplication.class, args); } }
创建测试用例,执行对应的规则
@Component
public class YourClass{
@Resource
private FlowExecutor flowExecutor;
public void testConfig(){
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
}
}
目录情况如下图:
从上面案例可以看出,LiteFlow 的使用非常简单,学习成本也很低。
8. 结语
相信大家看到这里对规则引擎一定有了一定的认知,并且能够根据自己业务选择适合自己业务的规则引擎,如果大家感觉有帮助,请大家点赞收藏+关注,有问题可以在评论区评论哦!
来源:https://juejin.cn/post/7434329177082789914
作者:CoderJie