规则引擎设计与实现
这是我参加【第五届青训营】伴学笔记创作活动的第六天
前言
规则引擎是一种嵌入在应用服务中的组件,可以将灵活多变的业务决策从服务代码中分离出来。通过使用预定义的语义模块来编写业务逻辑规则。在执行时接受数据输入、解释业务规则,并做出决策。规则引擎能大大提高系统的灵活性和扩展性。
规则引擎的实现需要在满足大容量、高请求、低延迟的基础上尽可能做到简单易上手。本次课程将会带领大家实现一个简单版的规则引擎
课程目标
- 理解规则引擎的组成部分及应用场景
- 理解规则引擎的核心原理- 编译原理的相关概念
- 设计并实现一个规则引擎- YoungEngine
- 结合之前所学课程,实现一个Web版规则引擎
- 认识规则引擎 探究规则引擎的由来、优点以及应用场景,简单了解规则引擎的组成和实现原理
- 编译原理基本概念 程序员三大浪漫之一:编译原理。介绍编译,词法分析,语法分析,抽象语法树等概念
- 设计一个规则引擎 设计规则引擎YoungEngine,明确其对词法,语法的支持,设计编译和执行的流程
- 规则引擎的实现 实现规则引擎YoungEngine的各个部分,并介绍其中的几个重点实现思路
认识规则引擎
规则引擎的定义
规则引擎是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入,解释业务规则,并根据业务规则做出业务决策。
这是没有使用规则引擎之前
使用规则引擎后
优点:解决开发人员重复编码的问题,业务决策与服务本身解耦,提高服务的可维护性,缩短开发路径,提高效率。
组成部分
数据输入 支持接受使用预定义的语义编写的规则作为策略集,比如"price>500",接受业务的数据作为执行过程中的参数,比如价格,标签等
规则理解 能够按照预先定义的词法、语法、优先级、运算符等正确理解业务规则所表达的语义
规则执行 根据执行时输入的参数对策略集中的规则进行正确的解释和执行,同时对规则执行过程中的数据类型进行检查,确保执行结果正确
应用场景
风控对抗
与黑灰产的对抗过程中,策略研发和产品需要能够根据黑灰产特征进行快速识别和对抗。规则引擎作为风控系统的核心,使产研人员能够不断的调整和优化对抗策略,以实现最好的风控识别效果。
活动策略运营
业务活动的运营需要及时根据用户效果反馈进行运营策略的优化和调整。引入规则引擎后,可以将服务代码与业务运营逻辑解耦,提高运营策略的迭代效率。方便新玩法的探索和效果验证
数据分析和清洗
在数据分析系统中使用规则引擎可以便捷的实现对数据进行整理、清洗和转换。数据分析师可以根据不同的需求来自定义数据处理的规则,方便快捷的产出所需要的数据。
编译原理基本概念
理解是词法分析、语法分析
词法分析:把源代码字符串转换为词法单元(Token)的这个过程
语法分析:在词法分析的基础上识别出表达式的语法结构
执行是抽象语法树 表达式抽象语法结构的树状表示,对于一个表达式,抽象语法树一定是唯一确定的
输入输出是参数注入、类型检查
参数注入:在规则执行过程中,使用输入的参数值来计算语法树中的标识符节点值的过程
类型检查:验证执行的结果是否为合适的数据类型。在抽象语法树中,通常会验证某节点的子节点的数据类型是否合法
词法分析
词法分析就是把源代码字符串转换为词法单元(Token)的这个过程。
如何识别Token:有限自动机 (Finite-State Automaton)
有限自动机就是一个状态机,它的状态数量是有限的。该状态机在任何一个状态,基于输入的字符,都能做一个确定的状态转换。
语法分析
词法分析是识别一个个的单词,而语法分析就是在词法分析的基础上识别出程序的语法结构。
这个结构是一个树状结构。这棵树叫做抽象语法树(Abstract Syntax Tree,AST)。树的每个节点(子树)是一个语法单元,这个单元的构成规则就叫“语法”。每个节点还可以有下级节点。
抽象语法树
上下文无关语法 Context-Free Grammar
编程语言为什么不用人类的语言(自然语言),而是用上下文无关的文法呢? 因为
- 便于设计编译器。 客观上技术目前无法实现,如果使用了上下文相关文法,那就是真正实现了人工智能,NLP领域将会有重大突破。
- 便于代码开发维护。 如果开发出来的代码像高考的语文阅读理解一样,每个人都有不同的理解,那么,到底哪个才是作者真正想要表达的?如果人类都确定不了含义,那计算机同样也确定不了,最终结果就是错误执行或无法执行。
- 汇编语言/机器语言是上下文无关的。CPU执行指令时,读到哪条执行哪条。如果CPU需要考虑上下文,来决定一个语句到底要做什么,那么CPU执行一条语句会比现在慢千倍万倍。考虑上下文的事情,完全可以用户在编程的时候用算法实现。既然机器语言是上下文无关的,那高级语言也基本上是上下文无关的,可能有某些个别语法为了方便使用,设计成了上下文相关的,比如脚本语言的弱类型。在便于使用的同时,增加了解析器的复杂度。
a=0;/这是一个赋值语句,无论此语句的前后是什么代码,此语句所代表的操作是确定的。即给变量a赋值等于0
语言句子无需考虑上下文,就可以判断正确性。可以使用巴科斯范式(BNF)来表达
产生式:一个表达式可以由另外已知类型的表达式或者符号推导产生
内置符号:字面量(string、bool、number)标识符,运算符
一个基础表达式可以由常量(string、bool、number)或标识符(identifier)
一个乘法表达式可以由基础表达式或者乘法表达式*基础表达式组成
start:blockStmts ; //起始
block : '{' blockStmts '}' ; //语句块
blockStmts : stmt* ; //语句块中的语句
stmt = varDecl | expStmt | returnStmt | block; //语句
varDecl : type Id varInitializer? ';' ; //变量声明
type : Int | Long ; //类型
varInitializer : '=' exp ; //变量初始化
expStmt : exp ';' ; //表达式语句
returnStmt : Return exp ';' ; //return语句
exp : add ; //表达式
add : add '+' mul | mul; //加法表达式
mul : mul '*' pri | pri; //乘法表达式
pri : IntLiteral | Id | '(' exp ')' ; //基础表达式
巴科斯范式
BNF是描述上下文无关理论的一种具体方法,通过BNF可以实现上下文无关文法的具体化、公式化、科学化,是实现代码解析的必要条件。
<expr> ::= <expr> + <term>
| <expr> - <term>
| <term>
<term> ::= <term> * <factor>
| <term> / <factor>
| <factor>
<factor> ::= ( <expr> )
| Num
BNF本质上就是树形分解,分解成一棵抽象语法树
递归下降算法 Recursive Descent Parsing
递归下降算法就是自顶向下构造语法树
不断的对Token进行语法展开,展开过程中可能会遇到递归的情况
根据右上角的BNF,进行编译
price是id,处于pri中,所以从上面log开始找,一直向下找,最终找到id,然后找>,再向上找到cmp,在向下找const,向上找到log&&,同理得右边
类型检查
类型综合 根据子表达式的类型构造出父表达式的类型,例如,表达式A+B的类型就是根据A和B的类型定义的
左边就可以根据29和10得到类型是int
右边知道10是int,s1是string类型,不能确定表达式的类型
编译时检查 & 运行时检查
类型检查可以发生在表达式的编译阶段,即在构造语法树的阶段,也可以发生在执行时的阶段
编译时 需要提前声明参数的类型,在构建语法树过程中进行类型检查
int1:int;str1:string
执行时 可以根据执行时的参数输入的值类型,在执行过程中进行类型检查
int1:100;str1:"300"
设计一个规则引擎
设计目标
词法分析
语法分析
语法树执行与类型检查
总结:
经过此节课学习,了解认识了规则引擎,方便代码的运用,提高效率。本人还是不太熟练,所以实现一个Web版规则引擎对于我来说,现在不是很简单,所以还要多学习,多了解规则引擎,争取实现一个Web版规则引擎。