规则引擎的设计与实现| 青训营笔记

98 阅读7分钟

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

课程目标:

  1. 理解规则引擎的组成部分和应用场景
  2. 理解规则引擎的核心原理——涉及到编译原理的相关概念
  3. 设计并实现一个规则引擎——YourEngine
  4. 结合之前所学习的课程,实现一个 Web 版规则引擎(自行实现)

课程目录:

  1. 认识规则引擎

探究规则引擎的由来、优点以及应用场景,简单了解规则引擎的组成和实现原理。

  1. 编译原理基本概念

主要介绍编译、词法分析、语法分析、抽象语法树等概念。

  1. 设计一个规则引擎

从零开始设计一个规则引擎 YoungEngine,明确对词法、语法的支持,设计编译和执行的流程。

  1. 规则引擎的实现

实现规则引擎的各个部分,并介绍其中几个重点实现思路。

1. 认识规则引擎

一句话概括为什么需要规则引擎:静态的代码满足不了产品和运营动态变化的需求,所以需要研发一款动态可调整的规则引擎。

输入:计算规则、商品价格等

输出:积分

特点:规则简单、容易配置、可扩展、能够复用

规则引擎的定义

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

思考:规则引擎从某种角度上来说是嵌入到应用程序中的小程序,规则引擎的语法实际上是一种方便产品和运行编写和理解的 DSL 特定领域编程语言。我们要做的实际上是定义一种 DSL 并且实现它的解释器或者编译器。

好处:解决了开发人员重复性编码的问题,方便产品和运营人员快速直接编写业务规则。业务决策与服务本身解耦,提高服务的可维护性,缩短开发路径,提高效率。

规则引擎的组成部分

  • 数据输入

接受使用预定义的语义编写的规则作为策略集。

  • 规则理解

能够按照预先定义的词法、语法、优先级、运算符等正确理解业务规则所表达的语义。

  • 规则执行

根据执行时输入的参数对策略集中的规则进行正确的解释和执行。

规则引擎的应用场景

  1. 风控对抗

策略研发和产品需要能够根据黑灰产特征进行快速的识别和对抗。规则引擎的快速修改的特性能够让对抗黑灰产的规则快速上线。

  1. 活动策略运营

业务活动的运营需要及时根据用户的效果反馈进行运营策略的优化和调整。引入规则引擎后,可以将服务代码和业务运营逻辑解耦,方便新玩法的探索和验证。

  1. 数据的分析和清洗

在数据分析系统中使用规则引擎可以便捷地实现对数据的整理、清洗和转换。数据分析师可以根据不同的需求来自定义数据处理的规则,方便快捷的产出所需要的数据。

编译原理的基本概念

| 词法分析 | 语法分析 | 抽象语法树 | 类型检查 |

规则引擎的三个步骤:理解(词法分析、语法分析)、执行(抽象语法树)、输入输出(参数注入、类型检查)

思考:规则引擎执行的三个步骤相当于解释型语言的解释过程。

词法分析

词法分析就是把源代码字符串转换为词法单元 Token 的过程。

对中文和代码的词法分析

如何识别 Token?使用有限自动机。

有限自动机是一种状态机,它的状态数量是有限的。该状态机在任何一种状态,基于输入的字符,都能做一个优确定的状态转换。

有限状态机示例

基于状态机去遍历字符串的表达式,输出一系列的 Token。

思考:词法分析就是将人类书写的字符串转换成语法分析能理解的 Token。

语法分析

在语法分析的基础上,识别表达式的语法结构的过程。

对中文和代码的语法分析

抽象语法树:表示表达式的语法结构,以树来表示。每个节点是一个语法单元,这个单元的构成规则就叫做“语法”,每个节点还可以有下级节点。

抽象语法树的定义

上下文无关语法:语言句子无需考虑上下文,就可以判断正确性。可以使用巴科斯范式 BNF 来表达。

产生式:一个表达式可以由另外已知类型的表达式或符号推导产生。

内置符号:字面量标识符、运算符

一个基础表达式可以由常量或标识符组成。

一个乘法表达式可以由基础表达式或者乘法表达式 * 基础表达式组成。即:乘法表达式可以递归地展开为全部由基础表达式组成的嵌套表达式。

例如:a * b * c可以递归地展开成 a * b(a * b) * c

递归下降算法:递归下降算法就是自顶向下构造语法树,不断的对 Token 进行语法展开(下降),展开过程中可能会遇到递归的情况。

思考:递归下降算法就是按照 BNF 对语法的定义,由语法分析器将 Token 从左向右的顺序在语法树上从上到下逐级求得解,最后求得一棵倒挂的树。

递归下降算法的结果和预期语法树的比较

经过语法分析器将 Token 转换成计算机能够理解的类型之后,可以将其转换成机器代码或交给解释器执行。在交给解释器执行之前,还需要进行类型检查。

类型检查

类型检查的方法:类型综合,根据子表达式的类型构造出父表达式的类型。

合法的语法树和非法的语法树

类型检查的时机:编译时检查和运行时检查。

  • 编译时需要提前声明参数的类型,在构建语法树过程中进行类型检查
  • 执行时可以根据执行时的参数输入值的类型,在执行过程中进行类型检查

设计规则引擎

设计目标

设计一个规则引擎,支持特定的语法、运算符、数据类型和优先级,并且支持以上预定义语法的规则表达式的编译和执行。

预定义的语法、运算符、数据类型和优先级

下面是预先设计好的状态机

预先设计好的状态机

BNF 定义

BNF 定义

语法树的结构可以通过二叉树来表示。

一元运算符:左子树为空,右子树为右操作数

二元运算符:左子树为左操作数,右子树为右操作数

括号:左子树为空,右子树为内部表达式的 AST

语法树的执行与检查:先执行左子树,然后再执行右子树,最后根据根节点的操作得到根结点的值。

类型检查:在执行时检查 检查方法:在一个节点的左右节点执行完成后,分别校验左右节点的类型是否符合对应操作的类型检查预设规则。

总结

本次课程的目的是实现一个规则引擎,实际上一个规则引擎就是一个领域特定语言的解释器。为业务逻辑定制一个语言和解释器可以同时拥有解耦和指向性强的特点。

参考

  • 编译原理
  • Jetbrains MPS
  • 学员手册
  • 实践项目