精华内容
下载资源
问答
  • 架构设计模式(Architecture Patterns),是“从特殊到普遍”的、基于各种实际问题的解决方案而总结归纳出来的架构设计最佳实践,是一种对典型的、局部的架构逻辑的高度抽象思维;在合理的场景下恰当使用它们,避免...

    架构设计模式(Architecture Patterns),是“从特殊到普遍”的、基于各种实际问题的解决方案而总结归纳出来的架构设计最佳实践,是一种对典型的、局部的架构逻辑的高度抽象思维;在合理的场景下恰当使用它们,避免“重新发明车轮”,对技术解决方案有指导性作用,往往事半功倍。广发证券IT研发团队作为架构设计模式的坚定践行者,在各类证券业务中经常运用。Event Sourcing就是这么一个比较常用而重要的架构模式。本文介绍的虽然是金融业场景,但是“积分系统”相信对其他行业的开发者也不会陌生。技术团队尝试用Event Sourcing架构模式和基于Go构建的DSL“简单而优雅”的解决一个问题。

    在电商行业,积分几乎已经成为了一个标配。 京东、淘宝都有自己的积分体系。 用户通过购物或者完成指定任务来获得积分。累积的积分可以给用户带来利益,比如增加用户等级,换取礼品或者在购物时抵扣现金。

    在广发证券的金融电商运营平台中,积分同样是一个不可或缺的基础服务,很多应用都有和积分账户交互的场景。积分的用途也比较广泛,除了用在面向客户的服务中增加客户粘性和忠诚度外,积分也被用来支持内部的“游戏化”(gamification)运营,让数字化经营成为可能。 例如:公司的投资顾问可以通过编辑高质量的理财知识条目和回答客户问题获得积分,最终被换算回个人绩效收入。


    一个场景,是客户提了一个关于证券的问题, 如果投资顾问回答了这一问题,并且答案被其他用户收藏,就可以获得500积分作为奖励。 实践表明,积分的使用大大提升了投资顾问回答问题的积极性,提高了运营的效率。这其实是精细化运营、数字化经营的一个非常重要的基础设施。这个积分体系的存在,甚至改变、颠覆了传统企业对员工进行分派任务、管理、激励、计算个人绩效的机制。

    从技术的角度,怎样实现一个积分系统满足各种应用程序的需求呢? 虽然使用积分的场景不同, 有的面向客户,有的面向公司内部的理财顾问,进行抽象后的积分系统可以是相同的。 和银行账户类似, 一个用户的积分账户可以看作由账户类型, 表示余额的数字和一系列引起积分变化的流水帐组成。 根据这些共性,我们把积分实现为一个独立的服务,统一存储管理积分数据。 在和应用程序交互的方式上,最初的想法是积分系统为应用程序提供增加/扣除积分的接口, 由应用程序决定增加/扣除积分的数量。

    我们很快发现这种架构在应用程序中嵌入了积分规则逻辑,当积分规则改变时,应用程序需要随之改变。 比如,如果运营人员把上面例子中的500积分改变为1000分后,开发人员就需要升级应用程序。 在使用积分的应用程序数量多,运营需求变化快的情况下,这种应用程序和积分系统紧密耦合的架构增加了系统维护成本。

    典型的“事件驱动”场景

    无论是面向消费者客户的电商平台、航空公司顾客的飞行里数服务(mileage program)还是面向内部员工的“游戏化运营”平台,很显然,在技术层面都是一个典型的“事件驱动”场景 – 用户通常通过在各种各样的业务系统进行了一些活动,这些活动被记录到一个积分系统中“映射”成一定的积分。所以,积分系统设施的“使用者”,往往是其他的一些各式其色的、事前甚至无法预估的应用程序。

    技术架构的设计原则是这样:由不可预知的应用程序自己负责判断其用户所进行的活动有无“价值”,对于有价值的活动则以发起事件的方式异步通知积分系统,积分系统则负责实时收集事件并基于各种可能由经营管理者随时修订、配置、改变的积分规则对事件所包含的用户活动进行“簿记”(book-keeping)。

    我们采用了基于消息总线的架构设计, 应用程序和积分系统之间通过异步的消息总线关联。应用程序不包含任何积分规则,只负责向消息总线发布事件。 积分系统被实现为一个独立的服务,包含了所有的积分账户数据和积分规则。 积分系统向消息总线订阅事件, 然后根据设置的积分规则处理事件, 记录积分。这种架构使应用程序和积分系统呈松耦合关系,提升了系统的可维护性。 系统架构如下图所示:

    举一个例子说明记录积分的过程:某投资顾问在广发证券知识库的应用程序中回答了一个问题,并且该问题被一个客户收藏。 知识库应用程序向消息总线发布一个答案被收藏的事件。 积分系统在监听到这一事件后,根据事先配置的积分规则,向投资顾问的积分账户增加积分数量,记录积分流水。

    积分系统监听的事件并不一定由应用程序直接产生。对于复杂的积分规则,可能由其他服务处理应用程序的事件流后,产生新的事件流,再由积分系统处理。例如,需要对7月份连续3天登录的用户奖励50分。应用程序没有保存历史登录数据,只产生简单的登录事件。 大数据平台(对于积分系统而言是一个应用程序)可以根据保存的历史数据产生包含连续登录天数的事件, 发布到消息总线上后由积分系统订阅处理。 这体现了基于消息总线架构的优点,能把积分处理逻辑从应用程序中完全剥离出来,同时具有扩展性。

    积分类似虚拟货币,可最终换算成员工绩效或者消费者的某些形式的奖励,所以不能多记,也不能少记。为了达到这一目标,技术层面上需要解决消息被处理一次且仅被处理一次的问题。我们的消息总线采用的是分布式消息系统Kafka, 它具有比较好的容错性和扩展性, 但不直接提供这样的支持,需要在应用程序层面处理。 应用程序向kafka发送消息时可能因为网络的原因发送失败。

    为了避免丢失用户积分,我们要求应用程序在向Kafka发送消息失败后进行重试。但这样又有可能出现同一个积分事件被重复接收导致多记积分的问题。 我们的解决办法是应用程序在产生积分的事件中带上一个对用户唯一的uuid, 并且通过重发的机制确保事件最少被发送到Kafka一次。 在积分系统中根据uuid进行排重,丢掉uuid重复的积分事件,保证积分事件最多被处理一次。通过这样一种应用程序之间的协议实现了一个积分事件被被处理一次且仅被处理一次的目标。

    Event Sourcing 架构模式

    在实践中,我们有修正积分的需求。 比如, 由于bug, 应用程序错误的产生出了一些事件,需要减掉由这些事件而增加的积分。直接的方法是找出这些事件产生的积分,然后从账户中直接扣减。 但是这一方法在下面的场景中会导致错误:

    假设积分规则是用户首次登录奖励500分,当天内第2次登陆再奖励1000分。

    1. 由于应用程序错误,产生了登录事件L1,导致增加500积分

    2. 用户登录产生登陆事件L2。 积分系统发现当天已经出现过1次登陆事件L1, 根据规则增加了1000积分。

    管理员发现为L1不应该发生,直接扣除500积分,用户实际得分1000分。 这是错误的。 在没有事件L1的情况下,登陆事件L2只应该获得500分。产生这一错误的根本原因是积分的计算可能依赖于历史事件。 历史事件的变化将影响后续事件处理。

    解决这种问题的一种方式是:当历史事件发生了变化时, 回滚到该时间点前的历史状态,然后按照时间顺序重新处理之后的所有积分事件, 这类似于数据库系统中使用checkpoint和日志来恢复数据库状态的方式。Event Sourcing 概括了这种软件设计模式(详细内容可参考软件设计领域大师Martin Fowler的相关文章)。Event Sourcing 模式最核心的概念是程序的所有状态改动都是由事件触发并且这些事件被持久化到磁盘中。 当需要恢复程序状态时,只需把保存的事件读出来再重新处理一遍。

    积分系统遵照Event Sourcing模式实现。 积分的所有变化都由积分事件触发,所有积分事件都存储在数据库中。为了回滚积分账户状态,还需要保存积分账户的历史数据。我们实现的方法是在积分账户发生变化时,产生一条积分流水,保存了积分变化数量,以及积分变化前和变化后的总额。当需要回滚积分账户状态时,找到离回滚时间点最近的积分流水,恢复历史积分账户的总额,然后按照时间顺序逐一处理保存的积分事件,恢复积分账户数据。 下图展示了这一流程:

    下面是用命令行工具把积分账户状态恢复到2016-05-01之前,然后重新处理积分事件恢复积分的界面。

    在生产环境的运维经验表明,相对于手工直接修改积分账户数据, 这种修改历史积分事件,回滚账户状态然后重新处理积分事件的方式不但提高了准确性,而且简化了修正工作,节省了运维人员的时间。

    用Go构建DSL实现灵活的积分规则引擎

    由于接入的应用程序类型多样,积分规则会随着运营的开展而频繁变化。如果每次积分规则发生了变化,都要求对积分系统改动升级, 积分系统维护就会变成一项很繁琐的工作。 我们的目标是让积分系统保持足够的灵活性,当积分业务规则变化时,在大多数情况下可以不用改动升级积分系统。最理想的情况是运营人员通过简单培训后自己就能配置积分规则,不需要开发人员修改积分系统软件。

    为此我们开发了一个积分规则引擎, 通过提供一个积分规则描述语言,把积分的业务逻辑从积分系统软件中分离出去:

    下面首先描述积分规则描述语言的语法表示和存储方式, 然后描述规则引擎加载解释积分规则的流程。

    积分规则引擎首先需要提供一个让运营人员描述积分规则的语法。抽象的看,积分规则可以表示为一个元组: (积分条件,积分数量), 表示当满足设置的条件时,增加对应的积分数量。 很容易联想到积分条件可以用编程语言中的布尔表达式表示,积分数量用数值表达式表示。

    由于我们使用的是Go语言实现积分系统, 出于解析方便的考虑(Go自带了自身的语法分析库),我们采用了Go语言的表达式语法表示积分规则的条件和数量。 在积分规则的表达式中,Go语言的字符串、数字、布尔常量都可以直接使用。变量表示积分事件中的字段数据。比如,积分规则(event_type==“answer_is_liked”, 250) 表示当前积分事件类型(event_type)为answer_is_liked(答案被点赞) 时,积分条件匹配, 记录 250 个积分 。

    在定义了积分规则的语法表示后,还需要决定在哪里存储积分规则。最初考虑存放在文件中,很快发现如果把积分规则和积分数据存放在同一个数据库中就可以方便的利用数据库的一致性检查功能保证数据一致性, 这是保证软件系统长期正确运行的关键措施。 比如,通过数据库的外键设置,我们能保证每条积分流水指向一个有效积分规则,杜绝因为规则被错删,积分流水指向无效积分规则的情况。 下面是积分规则在数据库中表示的例子:

    上表第1行积分规则表示当积分事件是answer_is_liked时,增加250分;

    第2行要复杂一些,表示当积分事件是answer_question(回答问题),并且属于首次回答问题时增加积分, 如果是投资顾问,增加4000分,其他人员增加2500分。其中event_type是积分事件的字段; count_by_same_event_attr是在规则表达式中允许使用的函数,用来统计该用户的具有相同字段值的积分事件数量;data.originator_type 也是积分事件的字段,表示用户类型。

    为了增强扩展性,规则引擎提供了一套插件机制,可以用Go语言编写能用在规则表达式中使用的函数。比如上表第2行中的count_by_same_event_attr就是通过插件实现的,用来计算目前已经收到的具有相同属性值的事件数量。在实践中,当发现积分规则不能满足业务需求时,我们往往通过编写插件的方式来扩展积分规则的表达能力,而不是修改规则引擎的核心代码。

    在运营人员配置积分规则后,积分系统需要使用规则引擎解释执行积分规则, 主要流程是:

    1、积分系统在启动时加载所有应用程序的积分规则

    积分规则在被规则引擎加载后完成语法解析,在内存中解释执行。 这避免了在运行中访问磁盘或数据库引起的性能瓶颈。需要注意的是,虽然积分规则的语法和Go语言表达式相同,积分规则的语义却有变化。对于会引起Go语言抛出异常的表达式(e.g. 除 0),积分规则引擎解释为nil,避免了程序异常退出。

    2、监听消息总线,对于新收到的积分事件,逐个尝试匹配积分规则的条件。如果该积分事件能满足某个积分规则的条件,则增加由积分规则中的积分。

    下图表示了运行规则引擎记录积分的流程。

    可以看出,我们实际上构造了一个DSL(Domain Specific Language), 语法和Go语言的表达式一样,但是语义不同。积分规则其实是这一DSL编写的程序, 作为数据保存在数据库中, 在被规则引擎装载后又当作程序来执行。这里体现了“代码即数据”(code as data)的编程思想。

    技术栈:Go + Postgres + Docker

    1、Go 语言

    Go语言是为大规模系统软件的开发而设计的, 具有语法简洁,静态类型检查,编译快速,支持并发程序设计等特点。

    和JavaScript等动态语言相比,我们感觉在某些场景下,由于Go的类型系统比较复杂并且不支持范型, 编写的代码量会多一些。一个典型的例子是排序,使用Go的排序库时,一般需要实现一个sort.Interface, 包含有Len, Swap, Less 3个方法。 而使用JavaScript进行排序,往往只需要1行代码。

    但是和动态语言相比,Go的静态类型检查减少了很多运行时bug,节约了调试时间, 并且Go提供的工具比较完善,自带文档,格式化,单元测试和包管理工具。 Go的生态系统也比较成熟,第3方软件包丰富。综合来看,使用Go的开发效率并不会低太多。

    我们发现Go语言的静态链接特性非常适合docker部署,积分系统用docker打包后只有10M左右。相比于NodeJS打包后上百M的体积,采用Go语言大大节省了部署时间和资源。

    总的来说,我们对Go语言是比较满意的,将会继续在关键的系统服务中使用。

    2、Postgres

    在使用了一段时间的MongoDB后,我们希望在关键业务中采用有严格schema检查的关系型数据库。 Postgres是一个成熟的开源数据库,除了支持数据一致性检查和事务外,也支持JSON, 吸收了NoSQL的优点。

    在积分系统中,应用程序需要在积分事件中保存一些自定义的属性, 在查询积分流水时积分系统原样返回,由应用程序自行处理。 由于事先无法预知应用程序保存的内容格式,我们把这样的数据放在一个JSON字段中, 完全由应用程序控制。在数据存入之后, 通过Postgres的JSON操作符,我们可以方便的管理这些数据,比如,根据指定的JSON字段查询。

    除了使用Go、Postgres、Docker这些技术开发和部署服务,由于积分系统是为应用程序提供服务的,它天然需要通过API来支持其他开发者。 我们选择了用工具slate来制作API文档。下图是使用markdown编写,由slate转换成html格式的 API文档式样。

    总结

    积分系统并不是一个技术架构上复杂的系统,但是它是借鉴“游戏”实践而进行的数字化精细化经营的重要业务环节,相信在越来越多进行“互联网+”创新的垂直行业中会有类似的实践。具体的技术实现手段也很多,在此为便于行业内外读者的理解,我们对方案作了简化和抽象。

    然而,对相对简单的问题作“教科书”式的简练实现,遵循KISS(Keep It Simple,Stupid!)的原则,避免“过度工程”(over-engineering),也是我们的团队文化和准则。本文所介绍的Event Sourcing架构模式和DSL规则引擎,可以帮助我们在很多场景“简单而优雅”(simple but elegant)的解决问题。

    展开全文
  • angular6基于jsplumb的规则引擎流程设计实现-附件资源
  • 业务规则、配置化编程、政策引擎、规则引擎 目录 ruleEngine 1. 背景 2. 方案考察 2.1. 硬编码: 2.2. Drools: 2.3. Urule: 2.4. 自研规则引擎 3. 能力要求 4. 名词解释 5. 概要设计 5.1. 功能模块...

    [未完待续中,后续代码实现优先发布到GitHub]

    https://github.com/failgoddess/ruleEngine

    ruleEngine

    业务规则、配置化编程、政策引擎、规则引擎

     

    目录

    ruleEngine

    1. 背景

    2. 方案考察

    2.1. 硬编码:

    2.2. Drools:

    2.3. Urule:

    2.4. 自研规则引擎

    3. 能力要求

    4. 名词解释

    5. 概要设计

    5.1. 功能模块划分

    5.1.1. 公式推理器Calculator:

    5.1.2. 指标解析器FormulaExecuter:

    5.1.3. 动作执行器ActionExecuter:

    5.1.4. 模型执行器RuleModel:

    5.1.5. 模型构建器RuleModelBuilder:

    5.1.6. 模型加载器RuleModelLoader:

    5.1.7. 交互转换器:

    5.2. 系统设计

    5.2.1. 指标占位符

    5.2.2. JSON出参解析

    5.2.3. 关系运算符

    5.2.4. 技术选型

    5.3. 关键技术说明

    5.3.1. 逻辑式编程

    5.3.2. 分治策略

    5.3.3. 决策树

    5.3.4. 括号匹配算法

    6. 详细设计

    6.1. 数据模型

    6.1.1. 规则ER-图

    6.1.2. 关系数据库表结构

    6.2. 执行逻辑图

    6.2.1. 规则执行时序图

    6.2.2. 指标解析执行流程

    6.3. 缓存对象数据结构

    6.4. 枚举值配置


     

    1. 背景

    在项目步入成熟期,规则类需求几乎占据了业务所有需求的半边天。一方面规则唯一不变的是“多变”,另一方面开发团队对“规则开发”的感受是乏味、疲惫和缺乏技术含量。如何解决规则开发的效率问题,最大化解放开发团队成为一个新的挑战。

    规则底层采用规则引擎来实现。规则引擎是一种嵌入在应用程序中的组件,实现了将业务规则从应用程序代码中分离出来,并使用预定义的规则语义来编写业务规则。规则引擎接受数据输入,解释业务规则,并根据规则执行相应的业务逻辑。一个业务规则包含一组条件和在此条件下执行的操作,它们表示业务规则应用程序的一段业务逻辑。我们在业务中设置一个或者多个条件,当满足这些条件时触发相应的操作,规则引擎设计的初衷是可以将复杂多变的规则从硬编码中解放出来,以规则脚本的形式存放在文件或者数据库中,使得规则的变更不需要修改代码即可使用,做到最大程度的灵活。

    2. 方案考察

    现在市面上在做业务规则的过程中有多种实现方案:

    2.1. 硬编码:

    优点:

    稳定性较佳:语法级别错误不会出现,由编译系统保证。

    当规则较少、变动不频繁时,开发效率最高。

    缺点:

    规则迭代成本高:对规则的少量改动就需要走全流程(开发、测试、部署)。

    规则开发和维护门槛高:规则对业务分析人员不可见。业务分析人员有规则变更需求后无法自助完成开发,需要由开发人员介入开发。

    2.2. Drools:

    优点:

    策略规则和执行逻辑解耦方便维护。

    缺点:

    业务分析师无法独立完成规则配置:由于规则主体DSL是编程语言(支持Java, Groovy, Python),因此仍然需要开发工程师维护。

    规则的语法仅适合扁平的规则,对于嵌套条件语义(then里嵌套when...then子句)的规则只能将条件进行笛卡尔积组合以后进行配置,不利于维护。

    2.3. Urule:

    优点:

    可视化操作完善、功能强大

    缺点:

    不支持回溯:当前分支没有符合条件的之后不支持回溯

    不支持动态加载数据:例如门店有等级、店龄、区域等若干属性,业务规则具体是根据店龄不同给出结果还是根据等级,属于规则的业务范畴,调用方并不关心。要是每次需要将所有可能用于规则判断的数据全部由调用方传入,无疑降低了规则的灵活性。

    2.4. 自研规则引擎

    在学习机器学习中决策树的算法时可以生成决策树的决策流图。决策树整体分为两个阶段:通过算法计算找出规律将规律构建决策模型、传入新数据基于决策流进行新的决策分析。这一点给我带来了很大的思想启发,配置规则的过程作为决策树的构建阶段,新数据决策为决策树的预测过程。出现多个决策路径组合的为最终结果的场景也可以理解决策森林算法的演化。

    3. 能力要求

    规则引擎的设计主要分为两部分:一部分是规则的维护,包括规则的创建、修改、删除;一部分是规则的执行。规则的维护部分侧重点是页面,我们需要将用户在页面上的操作转换为内置规则并保存到数据库中。在规则执行的过程中调用方只需要选择规则传入相应的数据即可获得决策结果。结合整体需求,规则引擎应该有可扩展、易维护的特点,先将规则引擎的功能需要实现的功能点总结如下:

    指标部分(维护指标、计算执行指标)

    模型维护(模型即实体,包括模型的创建以及模型属性的维护)

    规则维护(包括对规则的增删改查)

    条件维护(对规则条件的增删改查)

    指标维护(对规则指标的增删改查)

    结果维护(对规则结果的增删改查)

    节点维护(包括静态节点和动态节点部分)

    基于规则版本的决策记录(基于历史规则查看判定过程记录)

    规则的版本控制

    根据技术考察和能力要求对我们的规则引擎提出了更为全面的要求:

    l 支持可视化的界面配置

    l 支持嵌套条件语义

    l 支持组合条件

    l 支持动态加载

    l 支持决策日志

    l 支持规则历史版本

    l 支持回溯

    l 支持决策森林

    4. 名词解释

    术语及缩略语 名词解释
    规则 每一个需要用于判定的业务场景就是一套规则,例如:可否邮寄判断;规则通常由每一个决策树的判定结果和操作符构成,例如:区域决策树AND(调整价决策树OR年份决策树);规则的本质是分类问题,由决策树和操作符构建的决策森林
    节点 每一个规则内可能影响决策判定流程和结果的一个影响因素(维度)
    结果 一个规则中所有决策树中可能返回的结果,也就是一个决策树数的叶子结点,例如:可否邮寄判断的结果集合是是和否
    决策树 决策树又称为判定树,是规则中的每一条判定路径,这个判定路径是根据不同节点以及条件执行不同分支路径,完整的判定路径分支、节点就是一棵决策树。考虑到不同连接接有同一个下一跳的情况决策树也被称为决策图
    操作符 条件运算符
    分支 一个分支包含多个连接是一组连接的集合。
    条件 条件由被比较值、操作符、比较值构成。名称参考12/6=2 被除数、除数、商
    连接 连接表示当前分支中一个节点在一组条件下要进入的下一跳,下一跳可以是另外一个连接也可以是结果。主要是达到执行对象切换的作用。同一个分支中的不同连接指向同过个下一跳逻辑关系为或的关系,同一个连接中不同的条件为且的关系。
    指标 是规则流程执行中的元数据。
    判定日志 每一次判定结果的日志记录
    判定数据 用于规则判定时的入参对象
    历史规则 规则的历史版本控制,每一次规则的修改都会引起一个版本变化,同一时间节点一个规则只会有一个生效的版本

    5. 概要设计

    5.1. 功能模块划分

    5.1.1. 公式推理器Calculator:

    用于计算公式例如((18)/9)9572>1008611计算,是指标解析器的底层实现,公式推理器一部分基于规则引擎实现、一部分基于手写运算符计算器实现

    5.1.2. 指标解析器FormulaExecuter:

    在公式类指标中用于公式的构建例如:目标库存为公式类指标,计算公式为平均周销*周数,由指标解析器将平均周销和周数替换成指定的数值

    在传入类指标中用于将调用方的输入值转化成指标项例如目标库存中周数指标

    在配置类指标中用于将配置到系统中的参数解析成指定指标

    在动作类指标中用于组件构造动作执行器的执行入参并调用动作执行器。

    5.1.3. 动作执行器ActionExecuter:

    根据动作的类型传入参执行方法或者发送相应的请求,并负责解析返回值;不变型动作的执行可基于动作执行历史记录;

    5.1.4. 模型执行器RuleModel:

    负责解析规则模型按照深度优先的规则执行决策树,流程树

    5.1.5. 模型构建器RuleModelBuilder:

    确定一个规则编码负责从数据表中将于这个规则相关的数据加载到程序中,并组装出可以用于执行的规则模型JSON

    5.1.6. 模型加载器RuleModelLoader:

    负责加载配置好的模型,规则当前版本的加载优先级为Cache —> Redis —>模型构建器,规则历史版本的模型直接加载历史版本中的模型JSON

    5.1.7. 交互转换器:

    前端页面本着页面友好的原则会采用多种类型的页面展示和实现,前端数据和后端模型的数据会差异比较大所以在前后交互的过程中采用交互转换器进行数据转换,每种样式的展示方式都会有一个相应的转换器。

    5.2. 系统设计

    5.2.1. 指标占位符

    ${123}:调用id为123的指标

    @{storeCode}:引用入参storeCode的实际值

    %{ now()}:系统内置函数的返回值,这个系统特指规则引擎内部

    #{123}:引用id为123的节点

    ["storeCode","1002"]  向被调用者传入参数storeCode为1002 

    5.2.2. JSON出参解析

    请求型指标会访问一个url发送请求获得非接口返回值是JSON形式的数据,这里采用JsonPath插件对JSON进行解析获取需要的数据。完整语法请参考JsonPath官方文档

    Map或对象:$.attr_name

    List< Map或对象>$. data[i]. attr_name

    i=*或者不配表示 取全部列表每一个item的attr_name逗号隔开

    i=5 代表取索引为5 的attr_name

    5.2.3. 关系运算符

    系统内置操作符用于经销规则的逻辑判断,系统内置的每一条指令都有一个操作符,它表示该指令应进行什么性质的操作。不同的指令用操作符这个字段的不同编码来表示,每一种编码代表一种指令。

    操作符 布尔 不区分字符串 字符串 数值 时间 列表  
    be 介于(闭区间)        
    bed 介于(开区间)        
    nbe 不介于(闭区间)        
    nbed 不介于(开区间)        
    cn 包含          
    ncn 不包含          
    en 为空
    nn 不为空
    eq 等于
    eqd 等于(不区分)        
    neq 不等于
    neqd 不等于(不区分)        
    ge 大于等于        
    gt 大于        
    in 在集合          
    nin 不在集合          
    le 小于等于        
    lt 小于        
    nre 不正则        
    re 正则        
    and 并且      
    or      
    not      
    xor 异或      
    lp 左括号            
    rp 右括号            
    add        
    sub        
    mul          
    div          
    acm 取余          

     

    5.2.4. 技术选型

    技术选型目前规则引擎领域开源的主要有Drools、IKExpression、Aviator、QLExpress、SimpleEL、Groovy、Fel等,鉴于Fel表达式求值语法简洁。同时其优异的性能表现,规则中心选型Fel。执行100 万次表达式求值的性能表现对比如下图:

    5.3. 关键技术说明

    本系统会用到:依赖性任务排序的算法(用于指标依赖执行顺序的执行顺序)、决策树算法、分治策略、括号匹配、策略模式、逻辑式编程、JSONPath、DFS算法、Cache、Fel。

    5.3.1. 逻辑式编程

    算法+数据结构=程序这是Pascal设计者Niklaus Wirth的一本著作的书名,它刻画了过程式尤其是结构化编程的思想。后来Robert Kowalski进一步提出:算法=逻辑+控制。其中逻辑是算法的核心,控制主要用于改进算法的效率。在逻辑式编程中,程序员只需表达逻辑,而控制交给编程语言的解释器或编译器去管理。

    本规则引擎采用逻辑式编程的思想,由业务人员提供业务规则,开发负责将业务规则整理转义成解释器可解释执行的配置文件,在交由解释器和编译器解析执行。则可以做到业务只考虑规则构建、开发需表达逻辑、而执行交给解释器或编译器去管理,当调用方选择规则模型传入初始化数据后有加载器加载指定模型、执行器用相关的决策路径来控制执行返回规则结果达到一部分业务流程可配置的目的。

    5.3.2. 分治策略

    经销规则模块执行器采用分治策略实现深度遍历的决策树。分治策略是对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。

    分治法的基本步骤 分治法在每一层递归上都有三个步骤:

    分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

    解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

    合并:将各个子问题的解合并为原问题的解。

    5.3.3. 决策树

    决策树机器学习预测方法,主要用于对离散型数据的分类。一个值经过相应的节点测验会跳过假分支进入真分支,所以一组值经过决策树以后,就会形成从树跟到结果节点的一条唯一路径。所以它除了可以对输入进行分类之外,还能给出如此分类的解释。算法实现分为:特征选择、决策树的训练构建决策模型、模型对新数据测预测;

    规则引擎的数据维度抽取参考特征选择,也是在硬编码中用于if判断的数据字段;决策树模型的构建采用业务人员在实际工作中的经验规则;传入数据由模型执行类比对模型预测的阶段;

    5.3.4. 括号匹配算法

    指标的公式是可以嵌套的所以需要来解析公式需要用到括号匹配算法:

    123 门店平均销量指标 入参门店编码 sku编码 天数

    456 门店可用库存指标 入参门店编码 sku编码

    789 门店需求量指标公式为:门店10天的sku需求量=10天门店最近30天平均该sku销量-门店前库存;可表示为 10${123(storeCode:@{storeCode},skuCode:@{skuCode},dayNumber:30)}-${345(storeCode:@{storeCode},skuCode:@{skuCode})}

    这个解析的过程是一个采用分治策略的递归解析过程;用到括号匹配算法来解析公式;

    原括号匹配算法只是在结构出判断堆栈是否为空用于判断是否成对出现,这里需要做一个改造每一次堆栈为空的时候表示这是一个最大单元也就是整个公式最多可以分为几段 门店需求量就可以分为两段 ${123(storeCode:@{storeCode},skuCode:@{skuCode},dayNumber:30)}和${345(storeCode:@{storeCode},skuCode:@{skuCode})}

    在利用分治递归的思想将每一段分别处理

    6. 详细设计

    6.1. 数据模型

    6.1.1. 规则ER-图

     

    6.1.2. 关系数据库表结构

    节点表

    字段 数据类型 长度 说明
    Id int8 64 节点表
    rule_id int8 64 所属规则ID
    Name varchar 64 节点标题
    note_type int4 32 节点数据来源类型:10传入20指标值
    note_category int4 32 节点类型:1可变、0不可变
    Attribute varchar 128 判定数据对象的字段
    note_category int4 32 节点类型:1可变、0不可变
    value_type int4 32 10数字20时间30字符串35不区分字符串40列表
    parameter varchar 255 参数用json表示,节点调用指标的参数只能是配置好的固定值或者传入节点的数据值
    quota_id int8 64 指标id
    Remark varchar 512 备注

    结果表

    字段 数据类型 长度 说明
    Id int8 64 结果表
    rule_id int8 64 所属规则ID
    Name varchar 64 结果标题
    Data varchar 128 结果数据
    result_type int4 32 结果类型10固定值20节点值30指标值
    node_id int8 64 节点ID
    quota_id int8 64 指标ID
    parameter varchar 255 参数用json表示
    Remark varchar 512 备注

    规则表

    字段 数据类型 长度 说明
    Id int8 64 规则表
    Name varchar 64 规则名称
    Code varchar 64 规则编码
    Content varchar 32 规则内容
    data_type varchar 32 结果数据的类型
    node_info varchar 32 支持的对象节点的描述
    node_attribute varchar 32 支持的对象节点的属性
    Remark varchar 255 备注

    决策树表

    字段 数据类型 长度 说明
    Id int8 64 经销商商品授权表
    rule_id int8 64 规则ID
    code varchar 64 决策树编码
    first_branch_id int8 64 第一跳ID(分支)
    name varchar 64 决策树标题
    remark varchar 255 备注

    运算符表

    字段 数据类型 长度 说明
    id int8 64 运算符表
    code varchar 64 编码
    operation_type int4 32 类型:1关系运算符2逻辑运算符3优先级运算符4算数运算符
    name varchar 128 运算符标题
    remark varchar 255 备注

    分支表

    字段 数据类型 长度 说明
    id int8 64 分支表
    node_id int8 64 节点ID
    node_type int8 64 节点数据类型
    name varchar 64 条件名称
    operation_id int8 64 操作符ID
    threshold varchar 32 阀值
    rule_id int8 64 规则ID
    tree_id int8 64 决策树ID
    remark varchar 512 备注

    条件表

    字段 数据类型 长度 说明
    id int8 64 条件表
    link_id int8 64 连接ID
    priority Int4 64 优先级
    name varchar 64 条件名称
    operation_id int8 64 操作符ID
    threshold varchar 32 阀值
    rule_id int8 64 规则ID
    branch_id int8 64 分支ID
    threshold_type Int4 32 阀值类型10固定值20节点值
    note_id int8 64 节点ID
    remark varchar 512 备注

    连接表

    字段 数据类型 长度 说明
    id int8 64 连接表
    name varchar 64 连接名称
    priority int4 64 优先级
    tree_id int8 64 决策树ID
    branch_id int8 64 所属分支ID
    next_branch_id int8 64 下一跳分支ID
    next_type int4 32 下一跳类型
    next_result_id int8 64 下一跳结果ID
    rule_id int8 64 规则ID
    remark varchar 512 备注

    判定数据表

    字段 数据类型 长度 说明
    id int8 64 判定数据表
    rule_id int8 64 规则ID
    rule_version varchar 128 规则版本号
    data varchar 128 判定数据
    result varchar 64 结果(不一定来源于结果)
    result_id int8 64 结果ID(为空则表示规则结果为和合成结果)
    history_id int8 64 历史ID
    factor varchar 128 决定因素
    remark varchar 512 备注

    判定日志表

    字段 数据类型 长度 说明
    id int8 64 判定数据表
    rule_id int8 64 规则ID
    rule_version varchar 128 规则版本号
    judge_data_id int8 64 判定数据ID
    flag bool 0 是否符合
    history_id int8 64 历史ID
    node_id int8 64 节点ID
    link_id int8 64 连接ID
    condition_id int8 64 条件ID
    branch_id int8 64 分支ID
    remark varchar 512 备注

    历史规则表

    字段 数据类型 长度 说明
    id int8 64 历史规则表
    rule_id int8 64 规则ID
    rule_version varchar 128 规则版本号
    start_time timestamp 64 开始时间
    end_time timestamp 64 结束时间
    remark varchar 512 备注

    指标表

    字段 数据类型 长度 说明
    id int8 64 配置表
    quota_type int4 32 指标类型10方法类20请求类30公式类40配置类
    name varchar 255 指标名称
    value_type int4 32 数据值类型10数字20时间30字符串35不区分字符串40列表
    java_method varchar 255 10方法类所配置的方法
    request_url varchar 255 20请求类请求路径
    request_method Varchar 255 20请求类请求方法,目前只支持get、post
    formula Varchar 255 30公式类计算公式
    deploy Varchar 255 40配置类配置
    parameter Varchar 255 参数用json表示
    feedback_rule Varchar 255 返回值取值规则
    necessary varchar 255 必须的入参也就是调用者必传的字段
    cache_ime varchar 255 请求型缓存时间
    remark varchar 512 备注

    6.2. 执行逻辑图

    6.2.1. 规则执行时序图

     

    6.2.2. 指标解析执行流程

    6.2.2.1. 公式型指标

    10*${123(attr1:12,attr2:${456})}+2

    10*

    ${123(attr1:12,attr2:${456})}

    123 的指标

    解析123指标需要的入参

    attr1=12

    attr2=${456}

    ${456}

    456 的指标

    456 的指标 执行

    123入参构建完成

    123 的指标 执行

    +2

    6.2.2.2. 请求型指标

     

     

    6.3. 缓存对象数据结构

     

    6.4. 枚举值配置

    枚举类型 枚举值 枚举描述
    节点数据来源类型 10 传入值
      20 指标值
    指标类型 10 方法类
      20 请求类
      30 公式类
      40 配置类
    结果类型 10 固定值
      20 节点值
      30 指标值
    下一跳类型 1 分支
      2 结果
    阀值类型 10 固定值
      20 节点值
    数据值类型 10 数值
      20 时间
      30 字符串
      35 不区分大小字符串
      40 列表

     

    展开全文
  • drools规则引擎设计

    千次阅读 2019-01-12 16:03:39
    规则引擎有很多,创建规则引擎的目的,简单的来说,是为了实现智能化,在规则改变的时候,业务代码不需要改变,不需要根据规则来编写新的代码。 规则引擎的计算,和神经网络算法相似,将规则按照LHS 和LRH 执行。 ...

    规则引擎原理:http://www.open-open.com/lib/view/open1417528754230.html

    规则引擎有很多,创建规则引擎的目的,简单的来说,是为了实现智能化,在规则改变的时候,业务代码不需要改变,不需要根据规则来编写新的代码。
    规则引擎的计算,和神经网络算法相似,将规则按照LHS 和LRH 执行。

    drools语法就不说了,来说一下规则怎么可以不需要通过文件来编写,直接将规则保存到数据库中,通过将数据库中的规则来动态的修改规则。

    drools使用

    • 1.有一个使用的场景,即:事件;
      2.有很多规则 规则
      3.每条规则有很多条件 条件
      4.每个条件是由一个元素组成
      组成部分: 事件 规则 条件 元素
      关系:元素是最小单元 ,其次是 ,条件,规则,事件 简单的描述: 一个事件是由多个规则组成 ,一个规则由多个条件组成 ,一个条件由多个元素组成

            元素:就是属性字段  eg。p_age  年龄  p_gender 性别
            条件:元素进行运算操作  eg。c_1: p_age > 10 年龄大于10 c_2: p_gender==男 
            规则:条件的组合集  eg, r_1: c_1&c_2 p_age>10&p_age<20  年龄大于10小于20   r_2:c_2  p_gender==男
            事件:规则的组合集  eg。 e_1:r_1||r_2  
      

    比如:根据不同的积分、性别、开户时间 等 奖励不同的礼券

    • 积分小于100,奖励10块
    • 积分100到200,奖励20
    • 积分200-500,奖励30
    • 男 奖励10
    • 女奖励15
    • 开户一年 奖励10
    • 最近3个月开户 奖励5
    • 男,积分大于500 奖励 200

    “根据不同的积分、性别、开户时间 等 奖励不同的礼券” ,就是一个事件;
    “男,积分大于500 奖励 200”,就是一个规则,其中包含的条件可以是多个,比如这条规则,要是男的,积分还要大于500;
    “积分小于100”,就是一个条件;
    “积分”,“男” “女” “开户时间”,这些就是元素;

    一个事件使用多条规则组成的,一条规则是用多个条件组成,一个条件又由元素组成的。
    在这里插入图片描述

    只需要维护元素,规则 ,条件,事件 就可以 维护一个规则模型。

    具体操作:

    1. 维护元素 元素类型 数值,字符串等
    2. 维护条件 保存条件组成的元素,操作,eg,字符串包含,相等,不包含;
    3. 维护规则 根据drools语法,构建规则 ; eg ,
    rule "rule_1"
        lock-on-active true
        salience 100
    when
        factData : FactData( (paramMap.get("gender") == "男") )  
    then
    	factData.setResultScore(10);
    	update(factData);
    end
    
    rule "rule_2"
        lock-on-active true
        salience 100
    when
        factData : FactData( (paramMap.get("setUpTime") > 365000)& (paramMap.get("gender") =="女"))  
    then
    	factData.setResultScore(10);
    	update(factData);
    end
    
    1. 维护事件 事件包含多个规则

    通过多模型的操作,将事件直接维护在数据库,当规则改动或者新加的时候,都不需要重新发布项目,加载drools 规则文件。

    展开全文
  • 基于EJB的业务规则引擎设计实现,这是我学习drools时找到的文档
  • 文章发布于公号【数智物语】(ID:decision_engine),关注公号不错过每一篇干货。...规则引擎在现实应用中实现了业务和代码分离,通过代理的方式将业务语言定义传递到系统中,维护和管理复杂的业务规则,从而起...

    文章发布于公号【数智物语】 (ID:decision_engine),关注公号不错过每一篇干货。

    来源 | 上海交通大学计算机科学与工程系

    作者 | 余锴

    摘要:规则引擎起源于基于规则的专家系统,属于人工智能的范畴,通过模仿人类的推理方式,通过试探性推理,使用人类能够理解的方式证明其结论。规则引擎在现实应用中实现了业务和代码分离,通过代理的方式将业务语言定义传递到系统中,维护和管理复杂的业务规则,从而起到支撑业务灵活多变的作用。本文提出一种新的轻型规则引擎,旨在及时交付并提供更为灵活和复杂的逻辑控制功能,并且能基于类似随机森林的方法优化业务规则,可以应用在自动化信贷审批。

    关键词:规则引擎;信贷审批;随机森林;

    01

    引言

    规则引擎起源于基于规则的专家系统(CLIPS:源于1984年NASA的人工智能项目),规则引擎通过模拟人类的思考方式,使用试探性推理方法得到结果,并使用人类能够理解的方式证明其结论。

    规则引擎自80年代至今,已经产生了多种商业及开源的产品,基本上都是基于Java开发, 既有商业产品,也有开源项目,其中最出名的是Drools。

    目前绝大部分规则引擎都基于RETE算法设计,RETE算法是一种归纳法推理算法,主要由规则库,工作内存和推理引擎三部分组成,通过构建网络保存中间信息,能极大的提高规则匹配的效率。自1982年Forgy提出该算法以来经过几次优化,受到广泛的认可,其他还有一些匹配算法比如LFA,TREAI,LEAPS,但效率比不上RETE。

    规则引擎目前主要的应用场景是CRM和审批系统。通过规则引擎企业得以快速灵活的制定和实施业务规则,从而快速的对市场做出反应或者是对审批流程进行快速的调整。

    近年来经济形势的发展要求企业提高更快更好的服务,而且同时需要控制快速上涨的人力成本,越来越多的企业正在引入人工智能技术,其中之一就是规则引擎。目前规则引擎基本是使用Java构建的,一般只有大型企业才有技术和资金实力来构建这样的系统。本文以信贷审批作为默认的业务场景,提出一种新的灵活的规则引擎设计,完全使用Python实现。

    规则引擎把复杂、冗余的业务规则与程序分离,做到架构的可复用移植。通过约定好的规范,接受用户的输入,解析用户的业务规则,最后执行解析好的业务规则作出决策。

    一般情况下,所有的业务规则都可以表达成类似IF … THEN的条件语句,但由于企业的快速发展,业务规则会迅速增加,维护和管理这些业务规则会变得越来越困难,而规则引擎将至少具有三个作用:

    (1)管理复杂的规则

    (2)使用过程不再需要二次编程

    (3)知识的管理和发现

    以上三方面可以使得业务人员把注意力放到真正有意义的领域,减少重复性工作。从业务的角度上说,重要的是知识的获取和复用,由于规则引擎将规则逻辑从代码中分离,使得业务人员可以着重考虑规则中蕴含的知识,而规则引擎的推理和运行机制又解放了维护人员浪费大量的时间在逻辑判断代码上,在实际应用中起到了巨大的作用。

    本文第一节进行概述,第二节简单介绍RETE算法及设计的动机,第三节具体介绍AJIT规则引擎结构的设计,第四节介绍引擎核心的三个算法,第五节将对本文做总结。

    02

    RETE算法

    2.1 算法介绍

    RETE算法是1982年由卡内基大学的Forgy提出的一种高效的模式匹配方法。由于其推理的效率很高,因为不少优秀的规则引擎都是基于RETE或者RETE的改良算法设计的。

    RETE采用了一种基于树排序的方式,将规则的LHS(左手边条件)进行网络编码,在初次编译的时候保存推理的信息路径,而在运行时只需要去查询和匹配这些结果就完成了推理,从而大大节约了推理的时间。

    2.2 相关概念

    算法分为两个部分:

    (1) 规则编译:根据规则集生成推理网络的过程。

    (2) 运行时执行:将数据送入推理网络进行筛选的过程。

    算法中的定义:

    (1) 事实:对象之间及对象属性之间的关系。

    (2) 规则:由条件和结论构成的推理语句。一个规则的IF部分被称为LHS,THEN部分被称为RHS。

    (3) 模式: IF语句的条件。IF语句有可能是由几个更小的条件组成的大条件,而模式是指不能再分割的最小的原子条件。

    算法是基于网络的:

    (1) Alpha网络:将规则中每一个模式的集合生成Alpha memory。有两种类型的节点,过滤类型的节点和其他条件过滤的节点。

    (2) Beta网络:有两种类型的节点Beta memory和Join Node。前者主要存储Join完成后的集合,后者包含两个输入口,分别输入需要匹配的两个集合,由Join节点做合并工作传输给下一个节点。

    RETE存在的不足:事实上,从规则引擎的意义来说,RETE在对知识的获取,验证和更新上是存在困难的,而在避免重复运行代码上也存在一定局限性。原因如下:

    (1) 网络结构

    RETE的网络是基于所有规则的模式的,RETE通过保存操作过程中的状态避免大量计算。因此当新增加规则时应当更新网络结构,重新编译,当业务存在频繁变化时,RETE的效果不会太好。

    (2) 黑盒子推理

    RETE更大的隐患是在于其推理过程。由于网络是基于所有事实生成的,一旦在应用时规则出错,定位问题将会耗费海量的时间,工作的重心又会回到代码层面,不利于知识的学习。

    03

    AJIT规则引擎结构设计

    本文提出的一种非常灵活的轻量规则引擎—AJIT(A just in time Rule Engine),完全使用Python语言实现,是在实际的业务中摸索出的一种创新性规则引擎。AJIT在性能上追求的是及时交付,其优点在于使用简洁的结构和算法,大量节约使用者和维护者的时间,使之把精力放在业务的思考上而非代码的修改上。并且Python可以方便地引入最新的机器学习或者AI的组件,使得AJIT更容易集成最先进的算法用于更复杂的决策。

    3.1 规则结构

    AJIT运行的基本单元是规则,每条规则都可以视为IF …(LHS) THEN …(RHS)的形式。

    LHS:规则触发的条件,可以视为自变量,有以下几种情况:

    (1)数据缺失。

    (2)数据错误。

    (3)数据正常的变量,又分为分类型变量(如性别)和连续性变量(如年龄)。

    RHS:规则执行的动作,可以视为因变量,有以下几种情况:

    (1)空操作:为了满足一部分特殊的情况,空操作不产生任何动作。

    (2)选择操作:进行标记动作,不影响决策结果。

    (3)决策操作:又可分为分类型决策(如审批通过, 审批拒绝, 人工调整)和

    量化型决策(改变金融方案的条件)。

    从存储的表结构来看,可以将规则分为扁平化规则和层级化规则:

    (1)扁平化结构:穷举LHS的组合,枚举规则的所有情况。这种结构适用于简单的规则,通过矩阵并行运算,执行速度非常快。

    (2)层级化结构:以决策树的结构进行层级化存储,通过迭代执行,避免维度的组合爆炸问题。

    3.2 规则逻辑块

    AJIT模拟人类一般处理问题的逻辑,把规则划分为三大逻辑块,R1,R2,R3。

    R1:强规则,即可以通过这类型的规则给出直接的审批决定,比如申请人如果在黑名单中,拒绝贷款。

    R2:弱规则,大部分此类的规则需要调整阈值做出决定,一般可以做出拒绝或者提高贷款要求。

    R3:关于规则的规则,这部分用来进行综合判断和控制。

    AJIT运行时会依次运行这三块逻辑,并允许在任何一个位置终止。通过这种划分可以让业务人员更容易理解规则的设置,发现问题时容易追溯。从引擎程序的角度来说,进行必要的划分也使控制变的更简单。

    3.3 数据表设计

    AJIT通过设计丰富的控制字段和状态字段使程序控制变的简单,由于规则表现为高度结构化的形态,因此可以存储在结构化数据库中。数据表可分为三个部分:数据表, 规则表, 策略表。

    (1) 数据表

    数据表描述规则依赖的数据定义,以及规则运行过程保存的数据。

    (a) LHS变量定义:定义LHS变量的数据源位置,对于静态数据源只读取一次,对于动态数据源则每次重新读取。

    (b) LHS查询记录:每次申请查询的LHS变量, 包含申请人的ID,申请编号,变量值, 查询的顺序等信息。

    (c) LHS组合表:每条规则对应一个LHS变量的组合。

    (d) 规则追溯表:每次申请运行规则情况,包含申请人的ID,申请编号,规则所属的逻辑块,是否命中,对应的策略,以及触发的原因等。

    (2) 规则表

    (a) LHS映射表:定义LHS变量的分箱映射,分为离散型变量和连续性变量。

    (b) RHS定义表:定义RHS的值, 操作的类型有指定分类,加法控制, 比例控制,决策的结果有分类, 界限, 累加几种,以及对应的变量值。

    (c) 规则定义表:定义规则的IF...THEN内容, 对于扁平化规则,存储规则对应的LHS变量的全部组合, 对于层级化规则,存储每一层变量的下一跳变量(每条路径至少有一个RHS)。

    (d) 规则属性表:定义规则的属性,包括规则的ID,名称,规则所属的逻辑块, 规则的控制字段(反转,强制命中,可执行),规则的重要性,规则修改需要的权限,创建规则的用户, 规则属于的规则集。

    (3) 策略表

    (a) 策略定义:定义策略的名称和作用。

    (b) 策略匹配表:存储策略到规则集的映射。规则集是规则的一个分类,一个规则可以属于多个规则集。

    04

    引擎核心算法

    引擎的核心算法构成完成了规则引擎的基本功能,负责规则导入,运行和优化的相关功能。

    4.1 冲突解决

    冲突解决算法完成规则的导入动作,要保证添加新规则之后,引擎决策的结果仍然是一致的。

    从集合的角度来看,规则是由LHS变量集合触发,得到RHS变量集合的过程,本质上同样的LHS定义应该得到近似的RHS结构.因此首先要根据LHS集合的情况进行匹配,而后比较RHS的情况。

    RHS比较的结果可以分为四类:冲突, 分歧, 细化, 简化。冲突指LHS集合完全相同时,决策结果的矛盾(比如通过和拒绝),不允许存在冲突。分歧指LHS集合相交,决策结果的矛盾。细化指新的LHS集合包含了已有的LHS集合。简化指新的LHS集合属于某个已有的LHS集合。

    图4-1LHS集合的四种情况

    伪代码:

    Ifset(New.LHS)&set(Old.LHS) == Null:

    write(New) into DataBase #互斥,不存在任何冲突

    Else ifset(New.LHS)==set(Old.LHS): # 重合,可能存在冲突

    If New.RHS contradict Old.RHS:

    Cancel New Or Overwrite Old # 冲突,二者取其一

    Else if set(New.RHS) != set(Old.RHS) :

    warning # 仅给出警告,规则仍然可以写入

    Else ifset(New.LHS) intersect set(Old.LHS): # 相交

    intersect_LHS = set(New.LHS) intersect set(Old.LHS) # 共同的LHS部分

    If intersect_LHS.RHS_New != intersect_Old.RHS Or contradict:

    warning #仅给出警告,规则仍然可以写入

    Else: # 包含与被包含

    If New.LHS belongs to Old.LHS:

    suggest_check: reduction (简化规则建议)

    Elif New.LHS contains Old.LHS:

    suggest_check: enhance (细化规则建议 )

    冲突是由相同的信息得到不同的结果,只能选取其一;分歧是由不同额外的信息导致不同结果,可以同时存在,但是需要业务人员持续关注;简化或者细化是对原有规则的修改,这几部分工作均可以由根据实际业务情况逐渐使用机器学习的方法自动化。

    4.2规则流水线

    根据预先指定的策略,根据规则的逻辑块及依赖关系组织规则的运行。

    图4-2 规则流水线

    伪代码:

    step1根据策略选出待执行的所有规则。

    step2将规则根据逻辑位置按顺序分块(R1,R2,R3)。

    step3 For R in [R1,R2,R3]:

    创建同步任务列表 sync_task = []

    创建异步任务列表 async_task = []

    step3.1  (同步规则)执行规则顺序>=0的规则 # 规则顺序为0的优先级最高

    If rules are hierarchical rules: #层级化规则

    For rule in rules:

    sync_task.append(规则)

    Else if rules are flat rules: # 规则是扁平化规则

    rule_matrix = load(rules)

    sync_task.append(rule_matrix)

    step3.2  (异步规则)执行规则顺序 = -1 的规则

    If rules are hierarchical rules: #

    For rule in rules:

    task = register_async_task(rule)

    async_task.append(task)

    Else if rules are flat rules:

    rule_matrix = load(rules)

    task =register_async_task(rule_matrix)

    async_task.append(task)

    step3.3执行规则

    For task in sync_task:

    sync execute task #同步阻塞的方式执行任务

    For task in async_task:

    async execute task #异步并行的方式执行任务

    容易看出,引擎的执行时间只受限制于每个规则逻辑块中的同步规则(需要阻塞式顺序执行的规则)。这些规则一般是选择性规则或者是比较复杂的规则,这部分是规则流水线的时间瓶颈。

    4.3 规则优化

    利用类似随机森林的算法,通过对引擎内的规则进行总结和随机实验进行策略调优和知识发现。

    图4-3 规则优化数据结构图

    关键字段说明:

    F1~Fn:使用的LHS变量。

    FG1~FGn:根据基本的LHS变量衍生出的LHS变量。

    Rule:基于Fn的组合。

    Strategy: 基于Rule的组合。

    Label: 机器学习的目标。

    伪代码:

    Step1参数指定:

    随机样本N, 用来建决策树所使用的记录数。

    随机变量维度m. 总的规则数M, 满足m <

    Step2随机生成树 # 数据维度上的随机优化

    For N in sample_over_records():

    For m in sample_over_features():

    y = label_choose()

    random_tree =make_decision_tree()

    For tree in existed_trees:

    If random_treebetter than tree:

    suggest(random_tree)# 建议更优的策略

    Step3优化生成树

    set_criteria() # 选择一个优化指标,比如信息值

    feature = get_current_lhs() # 获取当前树用的LHS集合

    y = label_chose() #选择一个目标变量

    optimize_decision_trees() # 优化树的分类

    Step4随机森林

    trees = make_random_trees(tree_number) # 根据指定的数量生成n棵树

    y = label_choose() #选择一个目标变量

    for tree in trees:

    predict.append(vote by tree) #所有的树进行投票预测

    从业务上看,该算法提供三方面的支持:

    (1)规则的阈值优化:通过树的优化,修改最初规则配置。

    (2)新的策略发现:找到更有效的策略。

    (3)组合模型预测:多棵树的投票,利于在缺失数据维度较多的情况下更好的决策。

    05

    总结

    本文介绍了RETE算法在实际应用中的一些困难,并提出一种新的规则引擎AJIT, 通过流水线式的结构设计使得AJIT可进行更复杂的逻辑控制,并且可以很灵活的响应业务需求.以下从四方面总结AJIT的实践经验:

    (1)工程实施

    集成性。由于Python语言有数量众多的包,因此数据清洗,矩阵运算,异步调度,服务部署等功能完全可以实现,仅有同用户交互的前端技术需要用到JavaScript和HTML5。

    解耦性。每个功能块都是相对独立的,因此实际工程可以根据实际情况最小化的实施。

    (2)算法学习

    由于所有的数据都是结构化存储的,因此可以非常方便的进行用算法学习来替代人工来进行知识发现.通过可视化交互,业务管理人员可以理解规则引擎的建议,并做出合理原则。

    (3)功能扩展

    规则的执行过程是高度灵活的,Python在网络爬虫和人工智能方面有天然优势,我们曾经在规则引擎中插入问卷验证用户的真实身份, 问卷的内容则是通过程序实时爬取的。资源允许的情况下,任何规则都可以灵活调用各种算法来扩展功能。

    (4)性能

    现实中,用Pandas数据框(毫秒级/操作)结构代替Numpy矩阵(50微秒级/操作)进行实施,因此引擎的运行速度较慢,每个申请的整体运行时间在15s左右, 但对于实时要求并不高的审批申请业务来说,这个时间是完全能够接受的。事实上,AJIT设计并不适合追求极致的速度,考虑到要接入众多的算法,即使采用并行技术,总体时间仍然会受限于最慢的算法。

    AJIT引擎是在实际应用场景下催生出的一种规则引擎,三种基本算法还需要不断的调优才能达到有效帮助人工提高效率,帮助企业做出更有效的决策。

    从AJIT引擎本身特性来说,对于实时性要求不高的场景都可以进行扩展的研究和应用。

    参考文献:

    [1]Charles.Forgy. Rete: A Fast Algorithmfor the Many Pattern/Many Object Pattern Match Problem, Carnegie-Mellon, 1982

    [2]Alex Rupp. The Logic of the BottomLine: An Introduction to The Drools Project,TheServerSide.com,2004

    [3]Jboss Drools Team.DroolsDocumentation:Chapter5 Hybrid Reasoning, 2016

    [4]Max.Tardiveau. RETE Business Rule Engines,AutomatedBusiness Logic LLC, 2012

    [5]鲍金玲. 基于规则引擎技术的Rete算法的研究,《科技信息》,2008,32(32):90-90

    [6]孙懿青. 基于规则引擎的自解析匹配推理原型系统研究,南京师范大学,2006

    [7]张剑,孟波. 基于规则引擎的一种智能工作流系统研究,《计算机工程与设计》, 2006, 27(14):2591-2593

    [8]赵凡. 基于规则引擎的研究与应用,《多语种信息技术研究室, 2007》

    星标我,每天多一点智慧

    展开全文
  • 文中提出了一个基于规则引擎的、结构灵活、具有良好的扩展性和易用性的嵌入式操作系统配置模型,而且在Vx-works系统平台下实现了应用。同时还对规则引擎,Vxworks Workbench,XML,XML Schema以及Eclipse插件开发等一...
  • 前段时间,公司项目需要,用了差不多接近一周时间在angular6中实现了一个规则引擎流程拖拽设计,整体效果如下图所示: 核心代码如下: 1.界面左侧规则节点拖拽到右侧生成: //定义左侧规则节点拖放...
  • [b][color=#FF0000]规则引擎实现方案[/color][/b] [img=http://img.my.csdn.net/uploads/201207/02/1341218446_3634.jpg][/img] 如果规则变更 直接在图片中的界面上 直接修改规则即可,然后编译发布,支持热部署
  • 基于Java规则引擎技术的扩展开发工具的设计实现
  • 文章提出了一个基于规则引擎的、结构灵活、具有良好的扩展性和易用性的嵌入式操作系统配置模型,而且在Vxworks系统平台下实现了应用。同时还对规则引擎、Vxworks、XML、XML Schema和Eclipse插件开发等一系列相关技术...
  • 协同规则引擎设计,夏烨晗,,论文给出了协同规则引擎的总体设计,该引擎是基于协同运行平台的协同规则引擎,可以实现对网络融合环境下不同网元以及消息事件的
  • 使用WEBLOGIC PORTAL规则引擎实现动态业务逻辑简介  业务应用的需求总是随着业务环境的变化趋势而不断地改变。决策很少是一成不变的,并且竞争压力要求业务逻辑的设计实现具有灵活性,以快速地适 应不断变化...
  • 本文阐述了某物流平台消息中心模块的而设计实现。采用了Web Service,SOA,ESB技术
  • 本 solution 给出了一种利用 C# 和 WWF 实现规则引擎的一种可能的实现方案,需要再进一步进行设计 设计时的开发还没有考虑,不过运行时已经有了方案,那设计时自然也很好搞定了
  • 计算到达节点设计(方向条件规则) 节点的方向条件,就是节点在发送的时候遇到分支,如何让系统选择业务场景下所需要到达的节点。 方向条件控制规则设计 关键字:ccbpm方向条件控制规则,方向条件控制方式。 解释...
  • groovy-bool-rule是一款开源的用于计算复杂布尔表达式的规则引擎,使用groovy动态脚本引擎设计实现。 github: https://github.com/JavaPentesters/groovy-bool-rule 1、基于groovy实现的复杂布尔表达式规则引擎 在...
  • 自定义的规则引擎,JEXL的实现

    千次阅读 2019-03-06 11:33:27
    虽然drools规则引擎强大,但是部署和开发难度还是很大的,学习曲线也非常陡峭。 所以,想自己定义一个简单的规则引擎设计思路就是用json文件定义一下规则,能够支持动态逻辑,而不需要修改程序。 所以就有了...
  • 引擎了提供应用级的接口让应用程序获取感兴趣的RFID数据,通过接口应用程序可以定制数据事件周期的边界、数据过滤规则,数据分组规则、数据统计规则、数据的格式、数据的发送方式以及选择不同的数据源。  引 言 ...
  • 项目中需要设计开发一个规则引擎服务,于是调研了业界常用的规则引擎。常见的规则引擎如下:Ilog JRules是最有名的商用BRMS;Drools是最活跃的开源规则引擎;Jess是Clips的java实现,就如JRuby之于Ruby,是AI系的...
  • 设计了基于规则引擎的工作流模型REBWMS的体系结构,流程管理和规则管理及规则引擎服务模型,并对此系统下流程动态修改策略进行了研究。最后设计实现了一个网上订单的原型系统对REBWMS模型的可行性进行了验证。
  • drools规则引擎

    2019-03-09 19:21:45
    drools设计实现电子书让你对drools的设计和技术有更深入的了解。
  • 引擎了提供应用级的接口让应用程序获取感兴趣的RFID数据,通过接口应用程序可以定制数据事件周期的边界、数据过滤规则,数据分组规则、数据统计规则、数据报告的格式、数据报告的发送方式以及选择不同的数据源。...
  • 通常的业务规则我们使用If then的形式来描述,而现实生活中的企业业务决策要复杂得多,一般由多个规则组成,而且其复杂性很难直接通过经典的基于rete的规则引擎利用其推理能力执行多个if then语句来解决。...
  • 基于web应用来说,通常分为三部分:界面层、业务逻辑层和持久层。 所有的开发平台一般都是在这三方面...业务逻辑层:强调逻辑调整的便利性,一般采用动态语言或者规则引擎实现逻辑的配置。 持久层:采用领域模型,根
  • 该片文章只是抽取drools中java代码实现的一些代码结构,帮助我们理解drools是如何实现rete算法的。 该部分只是抽取ReteooStatefulSession工作过程中的代码架构 利用了多线程设计的一个代理模式(自己起的名字)  ...
  • 怎么设计一个规则,让每天新增的客户信息自动分级储存在表里面?然后根据需求文档,还要根据不同级别的客户信息,完成将客户信息推送到不同层级的销售人员的功能,比如说保费一次性购买达到120万以上的消费者,我们...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 721
精华内容 288
关键字:

规则引擎设计实现