规则引擎从认识到实践

2021-07-16 17:49:25   最后更新: 2021-07-16 17:49:25   访问数量:52




 

小明是一个兢兢业业的服务端程序员,有一天产品经理找到他说,我们要给用户发一条消息,消息的内容按照用户的积分分为三档,1000 以下的用户发“Sorry, you don't have enough points”,1000 以上,10000 以下的用户发“Thanks, we will give a prety gift for you”,10000 以上的用户发“Congratulation, we invite you to participate in our online events”。

 

于是,小明写了代码:

 

public void sendText(Person person) { if (person.getScore() < 1000) { System.out.println("Sorry, you don't have enough points"); } else if (person.getScore() < 10000) { System.out.println("Thanks, we will give a prety gift for you"); } else { System.out.println("Congratulation, we invite you to participate in our online activities"); } }

 

 

看起来代码很简单,小明高高兴兴地把代码部署上线,结果产品经理跑过来说,不行,咱的礼物不够,要把 1000 的阈值变 5000,10000 的阈值变 50000。

 

小明赶紧加班把这版改好上线,产品又来了,不行,文案不合适,需要调整。

 

这样三番五次一改再改,小明键盘一摔,对产品经理大吼:

 

git 权限给你开了,代码你来写!

 

你是否也有过这样的沉痛经历呢?更有甚者,经过一次又一次的修改,产品经理最终会把三个 if 扩充到十几个甚至几十个来回嵌套的 if 条件,这样的代码终将把你逼疯:

 

 

 

小明想:要是能让产品自己写代码实现这些逻辑就好了。

 

小明的想法可以实现吗?当然可以了,规则引擎就是用来解决这样问题的系统。

 

 

近来,低代码平台的概念被炒得火热,事实上这并不是一个新生概念,早在上世纪 60 年代,就已经诞生了通过编写简单的伪代码、表达式实现复杂的逻辑推理程序,这就是“专家系统”。

 

从设计理念上来看,专家系统与如今的低代码系统的初衷是一致的,那就是用程序加上学习成本很低的逻辑代码实现对专家的替代。

 

规则引擎就是一个用来简化代码逻辑的专家系统,用来分离商业决策者的商业决策逻辑和应用开发者的技术决策。通过将决策逻辑编写为更接近现实中语言的规则,存储在数据库或程序内存中,需要执行时取出规则并解析,从而实现小明让产品编写决策逻辑,动态修改的诉求。

 

比如:

 

rule "high score" when Person(score >= 10000) then System.out.println("Congratulation, we invite you to participate in our online activities"); end rule "mid score" when Person(score >= 1000 && score < 10000) then System.out.println("Thanks, we will give a prety gift for you"); end rule "low score" when Person(score < 1000) then System.out.println("Sorry, you don't have enough points"); end

 

 

你甚至可以设计一个交互界面,让产品在页面上轻松填写阈值与文案,自动生成规则存储在数据库中,这样,产品经理要添加或修改这些逻辑就再也不用找你了。

 

 

根据上面的讲解,我们知道,规则的一般形式就是 IF - THEN 操作,通过条件判断与具体操作完成决策的定义。但实际上,每一个具体的规则都不是一个简单的决策,而是由一系列决策以各种方式组成的决策网络,RETE 算法就是高效构建决策网络的算法,RETE 得名于拉丁文中的“网络”一词。

 

Rete 算法最初是由卡内基梅隆大学的 Charles L.Forgy 博士在 1974 年发表的论文中所阐述的,他的核心并不复杂。

 

Rete 算法巧妙地将逻辑推理的过程总结成有向无环图,每个 if-then 判断都可以视为是一个节点,然后将所有的节点以一定的方式连接在一起,这就构成了一张有向无环图。你可以仔细思考一下,无论是多么复杂的推理逻辑,都可以用不同的图将推理过程绘制出来,只是分支多少可能有所区别,事实上,这就是所谓的“决策网络”。

 

一张有向无环图中最重要的就是各个节点的组织,RETE 算法将构成这个逻辑的有向无环图的节点分为以下几类:

 

  1. RootNode -- 这张有向无环图的根节点;
  2. ObjectTypeNode -- 对象类型节点,保证所传入的对象只会进入自己类型所在的网络;
  3. AlphaNode -- 条件判断节点,只有符合条件才能向下传播;
  4. JoinNode -- 连接节点,将两个分支进行连接,相当于 and 操作;
  5. NotNode -- 过滤节点,过滤掉数组中不存在的元素;
  6. LeftInputAdapterNodes -- 将单个对象转化为数组;
  7. TerminalNodes -- 终结节点,说明已经完成所有条件的执行。

 

下面就是一个简单的 RETE 网络图:

 

 

 

 

基于 Rete 算法的规则引擎非常多,这里我们选用业内使用最为广泛的 Drools 来举例。

 

Drools 是在 Rete 算法基础上提出了 Rete 算法的面向对象版本 -- ReteOO 算法,并实现了一套 java 版本的规则库。

 

4.1 maven 依赖

 

首先,需要引入 maven 依赖如下:

 

<!-- https://mvnrepository.com/artifact/org.drools/drools-compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>drools-compiler</artifactId> <version>${drools.version}</version> </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-templates</artifactId> <version>${drools.version}</version> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${drools.version}</version> </dependency>

 

 

4.2 编写 drl 文件

 

package cn.techlog.testjava.main.drools.test import cn.techlog.testjava.main.drools.activities.Person rule "high score" when Person(score >= 10000) then System.out.println("Congratulation, we invite you to participate in our online activities"); end rule "mid score" when Person(score >= 1000 && score < 10000) then System.out.println("Thanks, we will give a prety gift for you"); end rule "low score" when Person(score < 1000) then System.out.println("Sorry, you don't have enough points"); end

 

 

4.3 创建 drools 工具类

 

package cn.techlog.testjava.main.drools.util; import cn.techlog.testjava.main.drools.video_edit.DroolsTest; import java.io.*; import java.net.URL; public class DroolsUtil { public static String getDrlString(String drlFileName) { StringBuilder drlStringBuilder = new StringBuilder(); try { URL url = DroolsTest.class.getClassLoader().getResource(drlFileName); File file = new File(url.getFile()); Reader reader = new InputStreamReader(new FileInputStream(file)); char[] buffer = new char[1024 * 1024]; int bytes = reader.read(buffer); for (int i = 0; i < bytes; ++i) { drlStringBuilder.append(buffer[i]); } } catch (IOException e) { e.printStackTrace(); } return drlStringBuilder.toString(); } }

 

 

4.4 编写测试代码

 

package cn.techlog.testjava.main.drools.activities; import cn.techlog.testjava.main.drools.util.DroolsUtil; import org.kie.api.io.ResourceType; import org.kie.api.runtime.StatelessKieSession; import org.kie.internal.utils.KieHelper; public class DroolsTest { public static void main(String[] args) { String drlStr = DroolsUtil.getDrlString("activities.drl"); KieHelper helper = new KieHelper(); helper.addContent(drlStr, ResourceType.DRL); StatelessKieSession kieSession = helper.build().newStatelessKieSession(); Person person = new Person(); person.setScore(10); kieSession.execute(person); person.setScore(100000); kieSession.execute(person); person.setScore(5000); kieSession.execute(person); } }

 

 

4.5 执行结果

 

Sorry, you don't have enough points

Congratulation, we invite you to participate in our online activities

Thanks, we will give a prety gift for you

 

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周都有精彩推文,全部原创,只有干货没有鸡汤

 

 

 

 






算法      技术分享      规则引擎      drools      rete     


京ICP备2021035038号