规则引擎原理详解 | 青训营笔记

367 阅读6分钟

“这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天

作为小白,想要成功实现规则引擎的第一步,明白规则引擎是什么(what),是为了解决什么样的问题(why),它的核心原理是什么,如何实现(how)。

规则引擎的核心原理涉及到的是编译原理的知识:词法分析、语法分析、类型检查、语法树执行,设计规则引擎实际上就是设计这四个步骤。

1. 理解规则引擎是什么:

1.1 要理解规则引擎,我们先说明什么是规则?

规则是一种决策结果,例如银行在对信用卡申请数据做审批时,规定如果申请人满35周岁,则增加5分,这就是一条规则。规则由业务人员制定,开发人员实现。该规则似乎只需if-else就可轻松完成,为什么还需要弄什么规则引擎呢。

1.2 为什么需要规则引擎?换而言之,规则引擎到底要解决什么问题?

分析:承接上个问题,一条满35岁加5分的规则if-else即可完成,那如果规则不止一条,数量十分庞大呢?可以多打几个if-else。那如果规则经常变化呢?可以业务人员变化规则后将该消息传递给开发人员,开发人员修改,有点麻烦且费时间。那如果规则之间嵌套严重,流程分支非常复杂呢?一堆嵌套if—else,除了自己没人看得懂,维护困难。

总结:由于传统方式开发和维护大量的逻辑判断来实现规则,存在以下问题:

  • 流程分支复杂,规则庞大,常规if-else难实现。
  • 规则不确定性,变更频率高。
  • 业务代码(规则实现)和逻辑代码冗杂,规则变更离不开开发人员。
  • 时间成本大,代码有隐藏风险。

由于以上问题的存在,迫切需要一种解决方法,于是规则引擎诞生。

1.3 规则引擎定义:

规则引擎是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接收数据输入,解释业务规则,并根据业务规则做出业务决策。

1675336276(1).png

1.4 规则引擎组成部分及应用场景?

1675336489(1).png

1675336612(1).png

2. 规则引擎核心原理?——编译原理的相关概念

2.1 规则引擎与编译原理的关系?

规则引擎的本质呢就是我们自己定义一套语法,然后去解析用这套语法写的表达式,然后根据解析的内容执行表达式。这个过程其实就是编译和执行的过程。

编译整个流程原理如下:

graph LR
源代码 --> 词法分析程序 --token串--> 语法分析程序 --语法树--> 类型检查和执行  

与规则引擎原理对比如下: 1675336700(1).png

整个流程梳理:
规则引擎需要通过语法分析和词法分析来把代码表达式转换为规则引擎本身能理解的东西,也就是抽象语法树,之后再执行该语法树 (执行即对树做一个遍历),执行过程中会根据我们提前设计好的语法做一个计算,在这个过程中我们需要与外界做一个交互,也就是输入输出。

2.2 词法分析

词法分析过程将源代码转换(切分)为词法单元(token)的过程。依据由状态机描述词法进行切分。总的来说,在词法分析阶段会有一个状态机,这个状态机就是对规则引擎所支持的词法的描述。词法分析的设计实际上就是状态机的设计。 image.png 基于状态机挨个遍历整个表达式,输出一系列的token给下一阶段(语法阶段)使用
实现步骤:

  1. 扫描源程序。(读入字符流到输入缓冲区汇总)
  2. 按照构词规则识别单词,输出单词本身及其种别码token
  3. 过滤无用成分。(如注释空格回车等)
  4. 调用出错处理程序。(识别定位词法错误,如非法字符违反构词规则)

2.3 语法分析

1675338560(1).png 语法分析是对词法分析切分后的token串识别出表达式构建语法结构(由语法树、抽象二叉树描述)的过程,词法分析的token串是否符合语言的语法规则,判定依据是对给定的输入字符串能否根据文法规则建立起一颗语法树。

怎么构建语法结构?怎么识别出表达式?根据我们定义出来的上下文无关语法,由巴特斯范式描述。

巴特斯范式:引入一种形式化符号来描述给定语言的语法,可以递归的表示自己,分析时候可以一层一层递归直到碰到终结符为止,可以放一些优先级的信息在里面。

2.4 抽象二叉树

怎么构建抽象二叉树?
依据每颗子树的构成原则(语法),由递归下降算法生成。 1675339344(1).png

递归下降算法:贪婪算法,尽可能早的匹配出一个语法单元。下降是指在当前优先级满足不了,下降一层到低优先级的过程,一层一层下推直到我们认识的内置符号的过程。 递归下降算法流程执行流程如下图所示: 1675340998(1).png

2.5 类型检查

主要是为了判断变量或者参数的实际类型和声明类型是否匹配的问题,发现问题,避免出现无法预期的结果

  • 怎么做?哪些方式?
    类型综合方式: 根据子表达式的类型构造出父表达式的类型,eg:(A+B)的类型是根据A,B类型定义。
  • 什么时候做?
    编译时检查(构造二叉树过程中识别左右子树是否合法)和运行时检查(真正拿到数据后遍历树时再做)
  • 编译时检查和运行时检查两者区别?
    编译时需要提前声明参数类型,这样再构造语法树过程中就可以检查出来类型不对。执行时:根据执行时的参数输入的值类型,在执行过程中进行类型检查

2.7 小结

将字符串形式的源代码,通过预先定义的语法词法,即用状态机做一个表示,定义一些文法(巴特斯范式表达),最后通过递归下降算法一步一步构造出抽象二叉树,也就是规则引擎能理解的形式

参考

[1] 再见了 ! if-else !拥抱规则引擎 - 简书 (jianshu.com)
[2] 规则引擎怎么解释最简单易懂?_qq_39763312的博客-CSDN博客
[3] 风控逻辑利器---规则引擎 - 掘金 (juejin.cn)
[4] 编译原理-词法分析_luckyliuqs的博客-CSDN博客_词法分析
[5] 编译原理---词法分析_瞿颖Blog的博客-CSDN博客_编译原理词法分析
[6] 编译原理学习之:上下文无关文法(Context-free Grammar)和下推自动机(Push-down automata)_暖仔会飞的博客-CSDN博客_下推自动机
[7] 编译原理-语法分析_luckyliuqs的博客-CSDN博客_编译原理语法分析
[8] 【编译原理笔记】抽象语法树AST图解 - jiftle - 博客园 (cnblogs.com)