最近,项目上遇到了规则引擎的使用场景,我们通过规则引擎实现接口的可配置编排,将接口编排从代码中抽离出来,通过界面拖拽配置实现,还可以借助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. 结语
相信大家看到这里对规则引擎一定有了一定的认知,并且能够根据自己业务选择适合自己业务的规则引擎,如果大家感觉有帮助,请大家点赞收藏+关注,有问题可以在评论区评论哦!