第1章介绍了规则引擎的一些基础概念和使用场景,本章将从系统架构和运行原理两个方面对规则引擎进行更深入的介绍。
不同的规则引擎在架构、算法和功能实现上都有所区别,本章所讲的架构和原理均基于Drools规则引擎的设计理念。虽然其他轻量级、重量级或商用的规则引擎的具体功能实现可能会与此有所不同,但基本理念相差不大,可以相互借鉴。
在第1章中我们已经知道当引入规则引擎之后,开发、部署、运营等操作流程会发生巨大变化,而引起操作流程变化的根本原因是业务系统的架构发生了变化。
规则引擎将多变的业务逻辑进行抽离,并单独处理,而一个完整的BRMS(业务规则管理系统)通常又包含规则库(知识库)、管理平台等组件。本节先来对比一下使用规则引擎前后,业务系统架构发生的变化。
首先,我们回顾一下未使用规则引擎时,业务系统的架构及处理流程。这里为了重点突出规则引擎的部分,对业务系统的架构做了简化处理,如图2-1所示。
在图2-1中,业务系统被简化为操作用户、业务系统和数据存储(数据库)三个部分,规则引擎要替换的便是业务系统中变动比较频繁、逻辑比较复杂的一部分业务逻辑,对应图2-1业务系统中的if…else…部分。
图2-1 简化的业务系统架构
在图2-1中,基本的业务操作包含以下步骤:
1)用户请求业务系统。
2)业务系统处理相对不变的逻辑A。
3)业务系统处理通过if…else…实现的(或频繁变动的)业务逻辑。
4)业务系统处理相对不变的逻辑B。
5)业务系统持久化数据并将结果返回给用户。
系统改造时,重点关注的就是业务系统中if…else…那部分。在未使用规则引擎时,如果此部分的业务逻辑频繁发生变动,就会导致业务系统频繁开发、测试、发布……
于是,我们引入了规则引擎,将if…else…部分的逻辑放在规则引擎中处理,与此同时也新增了规则管理平台等辅助系统。业务系统会通过API或其他形式与规则引擎交互。于是,基于规则引擎的业务系统架构如图2-2所示。
在图2-2中,将原有业务系统中的if…else…逻辑抽象成了规则引擎中的一条条规则,业务系统不再关心对这部分变化逻辑的处理,只关心输入数据、规则引擎API的调用及返回结果。
引入规则引擎之后,整个业务处理流程可概括为以下步骤:
1)用户请求业务系统。
2)业务系统处理相对不变的逻辑A。
3)业务系统调用规则引擎的API,并传入必要参数(Fact对象)。
4)规则引擎根据传入参数进行规则匹配处理,并返回结果。
5)业务系统处理相对不变的逻辑B。
6)业务系统持久化数据并将结果返回给用户。
图2-2 基于规则引擎的业务系统架构
这一改造也与设计模式中的开闭原则一致,都是将可扩展的部分单独剥离。业务系统通过规则引擎提供的API来调用对应的规则,设计时要尽量确保API不会因规则变动而发生大的变动。
对比图2-1和图2-2,会发现引入规则引擎之后整个业务系统架构有两方面的变化:第一,频繁变动的业务逻辑交由规则引擎管理了,当业务逻辑再变动时,不用发布应用系统了;第二,系统的架构因引入规则引擎而变复杂了。
第一方面的变化,原本就是引入规则引擎的目的,经过规则引擎的封装,平台的运营人员可直接参与规则的管理。最终实现大多数情况下无须重新发布系统,无须开发人员参与即可实现业务变动。
第二方面的变化就需要注意了,多引入一套系统就需要额外承担相应的风险,在技术选型时一定要考虑好这方面的因素。
关于架构部分,这里暂时不做过多展开,后续在实战过程中我会结合具体的场景更加深入地进行讲解。
了解了业务系统层面的架构之后,我们再深入一层,来了解一下规则引擎内部系统(包含BRMS)层面的架构及运行原理。
这里所说的规则引擎的系统架构,不仅包括规则引擎框架自身的功能组件(由框架提供),还包括外围的辅助系统。比如,轻量级的规则引擎没有规则库(知识库)和规则管理平台,如果企业自主研发了这部分功能,那么它们也同样属于规则引擎的体系。
Drools(7.x及之前版本)规则引擎提供了完整的BRMS,虽然Drools 8中已经移除了相关功能支持,但Drools 7.x中官方提供的BRMS的功能实现及思路与企业自主研发BRMS的实现及思路基本一致。因此,在系统架构层面,我仍然以Drools 7.x版本中的BRMS为例来进行讲解。
基于上一节的业务系统架构图,将业务系统部分简化,同时将规则引擎部分细化,于是就得到了更详细的规则引擎内部系统架构图,如图2-3所示。
图2-3 规则引擎内部系统架构图
在图2-3中,除了业务系统以外,主要部分还包括规则引擎(核心)、规则库(知识库)、规则管理平台等。需要注意的是,规则引擎可以通过多种形式读取规则,并非都是直接读取数据库的,这里只是示意。
结合图2-3来简单梳理一下规则引擎的运作流程:
1)规则引擎启动时加载规则库规则,后续规则变动时支持动态加载。
2)业务系统调用规则引擎的API,并传入事实对象(Fact对象)。
3)规则引擎通过模式匹配器对传入的事实对象和规则集合进行匹配,筛选符合条件的规则。
4)规则引擎通过执行器来执行符合条件的规则。
5)规则引擎执行完毕,将结果返回业务系统。
按照功能,图2-3所示架构可分为三部分:规则管理、业务系统和规则引擎。
规则管理,包括规则库和规则管理平台。规则库内的规则可通过规则管理平台由业务人员进行修改、发布等操作。一旦规则库被修改,需要触发规则引擎重新加载规则。通常规则的修改与加载分别由规则管理平台和规则引擎实现。
业务系统,在图2-3所示架构中与规则的具体实现可以说是解耦的。
规则引擎提供的API不会发生变化(除非升级版本),这里重点关注业务系统传递的事实对象。事实对象是规则引擎执行规则时的决策因子,通俗讲就是与规则匹配所需要的数据,可类比接口调用时的VO(Value Object,值对象)。
在Drools中,事实对象就是一个普通的JavaBean,规则的条件判断就是通过它所携带的数据来进行的。事实对象会被存储在Drools规则引擎的工作内存中,用于后续规则的匹配。在工作内存中,事实对象的公共(public所修饰的)方法是可以被直接调用的。
有了规则,也有了规则的条件判断所需的事实对象,剩下的核心功能就是规则引擎通过算法将事实对象和规则集合相匹配,也就是我要讲的规则引擎的实现原理。下一节就通过推理引擎模型和规则数据模型来讲解规则引擎的实现原理。
上一节介绍了规则引擎的业务架构,本节就从架构中最核心的规则执行(匹配)部分入手,基于推理引擎模型和规则数据模型来讲解规则引擎的实现原理。
本质上讲,这两个模型既从不同的维度上描述了规则引擎的实现原理,同时又互为补充。大家只有学习和理解了这些基本概念和模型,才能够更好地使用规则引擎或自主设计规则引擎。很多朋友无法快速入门规则引擎,原因之一就是缺少对规则引擎实现原理的理解。
这里以Drools规则引擎为基准来进行讲解。
规则引擎的核心实现通常由三部分构成:规则库(Rule Base,也称知识库)、工作内存(Working Memory)和推理引擎(Inference Engine)。这里推理引擎与决策引擎(Decision Engine)为同一含义。
在业务架构部分,我们已经看到规则库主要用来存储规则,工作内存主要用来存储事实对象,那么推理引擎是干什么的?顾名思义,就是基于事实对象和规则集合进行推理,筛选出符合条件的结果。
推理引擎的实现包含三个核心组件:模式匹配器(Pattern Matcher)、议程(Agenda)和执行引擎(Execution Engine)。
规则引擎的原理示意如2-4所示。
图2-4 规则引擎的原理示意
在图2-4中,工作内存中存储事实对象,对应的就是业务数据。规则库中存储业务人员定制的形形色色的规则。推理引擎将两者整合并利用模式匹配器、议程、执行引擎对事实对象与规则进行匹配,解决规则冲突以及规则执行的问题。
推理引擎通常有两种推理方式:演绎法和归纳法。Drools规则引擎使用的是演绎法,目前最高效的演绎算法当属Rete算法(Drools采用的Phreak算法便是基于Rete算法改进的)。所谓的演绎法就是从一个事实(Fact)出发,不断地根据规则(Rule)执行相应的操作。所谓的归纳法则是根据假设不断地寻找符合条件的事实。
图2-5展示了规则引擎的基本步骤。首先,初始数据(一个或多个事实对象)被输入工作内存中。接着,模式匹配器将数据和规则进行比较。如果同时激活多个规则(即发生冲突),则将规则放入冲突集合并解决。规划好的规则被顺序地放入议程中,由执行引擎进行执行。需要注意的是,模式匹配器的执行到最终规则的执行完成是一个循环执行过程,直到议程中的规则执行完毕,才输出最终结果。
经常有朋友询问:Drools规则引擎中规则的执行顺序是怎样的?通过图2-5,我们可以了解到规则引擎中的推理引擎首先会将所有的事实对象和规则集合进行匹配。这里只是匹配,并没有执行规则。所有匹配之后符合执行条件的规则会被放入议程当中,然后才统一执行,并循环这一过程。因此,规则的执行并不是每条规则从头到尾执行完之后再执行下一条规则的。
通过以上对推理引擎模型的学习,我们对规则引擎的原理已经有了基本的了解。接下来,我们从数据和规则之间的关系入手,进一步理解规则数据模型。
图2-5 推理引擎的基本步骤
推理引擎模型是从推理逻辑层面描述规则的,当然我们还可以从数据流转的维度来进一步了解规则的执行,也就是规则数据模型。这里所说的数据模型就是基于入参数据、依据规则处理、输出结果的运转机制。
从本质上讲,规则可以被抽象为一个函数,我们所说的事实对象便是函数的输入值 x ,规则便是函数的对应关系 f ( x ),规则执行之后的结果便是函数的输出。所以,一个规则可以通过如下函数来定义: y = f ( x 1 , x 2 ,…, x n )。
当然,实际执行的过程中,一个规则可能会涉及零到多个事实对象的输入、零到多个输出,同时也会存在各类规则之间的相互作用,比如,一个规则的输出结果也可以作为另外一个规则的输入,或一个规则的优先级较高导致另外一个规则无法执行等。
规则引擎的数据模型可以用图2-6来描述。
图2-6 规则引擎数据模型
图2-6中的事实对象已经多次提及,它是触发规则所需的业务数据,也就是规则触发的决策因子。
中间部分的规则(Drools中通常称为规则文件)由两部分组成:LHS和RHS。
LHS(Left Hand Side)部分是规则的条件部分,处理条件分支逻辑,可以理解为编程中的if判断,往往有零到多个判断条件。LHS用来判断输入的事实对象是否满足规则执行的条件,实践中很少会在此处改变事实对象的属性值或处理业务逻辑,最佳实践是只做条件判断。
判断条件的组成通常由一到多个模式(Pattern)构成,模式是规则内最小单位,比如判断事实对象中某个属性值大于指定阈值(比如Param 1 >10),这便是一个模式。在实践中,比较好的做法是将事实对象各个属性设计成可原子化配置的模式模块,然后由业务人员将其配置组合形成规则的条件部分。原子化模式的好处是能够最大化保证业务人员对规则配置的灵活性。
RHS(Right Hand Side)部分用来执行满足条件之后的业务逻辑,比如改变事实对象的属性值。图2-6中的输出结果便是由此部分来完成组装或改变的。
综上所述,规则数据模型是由事实对象、规则(文件)和输出结果构成的。我们在使用规则引擎时基本上都是围绕规则(文件)的语法和数据结构来完成业务逻辑实现的。
关于规则数据模型,我就讲这么多。理解规则数据模型,一方面有助于后续规则逻辑的实现,另一方面也有助于自主实现规则引擎。
除了上述理论模型之外,大家也有必要了解一下Drools使用的Rete算法。针对Rete算法部分,本书将在第11章中进行讲解。
至此,关于规则引擎理论部分讲解完毕,在后面章节中我们便基于Drools规则引擎来进行实战和实践。
在开始实战之前,我们来整体汇总Drools规则引擎所涉及的基础概念和组件,后面实战部分对这部分内容就不再解释,直接使用。
Drools中的基本概念和组件:
❑ 事实(Fact):也称Fact对象、事实、事实对象,是一个普通的JavaBean对象。它承载着业务系统与Drools规则引擎之间的业务数据传输功能,用于输入或更改Drools规则引擎中的数据。Drools规则引擎会使用它来进行规则的匹配、结果的反馈以及行为的执行。
❑ 规则(Rule):即用户定义的规则,支持多种形式,比如.drl文件、Excel文件等。其中至少包含触发规则的条件和规则执行的操作。在drl规则中,一般表示为if…then…。规则的if部分称作LHS,用于条件判断;then部分称作RHS,用于触发条件之后的业务逻辑执行。
❑ 模式(Pattern):LHS中判断条件的最小单元,也就是if条件中不能再继续分割的原子条件判断。
❑ 生产内存(Production Memory):用于存放规则的内存。
❑ 工作内存(Working Memory):用于存放事实对象的内存。
❑ 模式匹配器(Pattern Matcher):用于将规则库中的规则与工作内存中的事实对象进行匹配,匹配成功之后,存放在议程或其他类组件当中。
❑ 议程(Agenda):存放匹配器匹配成功后激活的规则,用于支持后续规则执行。
常用的基本概念就这么多,后面我们就开始真正的实战环节了。