分享

自建规则引擎在促销系统中的应用

 quasiceo 2018-10-06

自建规则引擎在促销系统中的应用

2018-07-19 17:19 操作系统

需求或目标

目标:提供一组数据,以及基于这组数据编写的任意规则条件,快速得出结果。

主要包含几个部分:

1 任意数据模型;

2 基于数据模型构造的任意规则;

3 根据传入数据运行规则、得到正确的行为或结果;

数据和规则的抽象及举例

数据的抽象

数据可以抽象为两类:一类是基础数据,一类是具有时间特性的流式数据,第一类数据可以直接应用于表达式中,而第二类数据需要在其基础之上做reduce规约操作得到规约结果数据。

下面以DR lender用户为例详细说明这两类数据:

类别1:基础数据

针对lender用户来说,基础数据包含id、姓名、手机号、等级、注册渠道、注册时间、是否绑卡、是否实名、绑卡时间、实名时间、风险偏好、投资金额、投资团等单值数据。

类别2:行为数据

针对lender用户来说,行为数据包含用户的投资、退团、注册、实名、邀请的人的投资记录等。这类数据的特征是具备时间特性,可以在其之上:(1)应用时间窗口(最近x天/y次,当前自然日/周/月等)限制; (2)进一步用规则条件筛选数据; (3)最后做count/sum/average等reduce规约型计算得出“规约结果”。

以用户的投资行为记录为例,规约结果如:

A. 最近10天的平均投资额;

B. 投资大于1w的总次数;

C. 最近30天投资大于2w的总次数;

D. 最近10次投资大于2w的总次数;

E. 当前自然月的首投大于1w;

F. 首次投资发生在注册后20天内;

以用户邀请的人的投资记录为例,规约结果为:

A. 所有单笔大于5w、且在[A,B,C]团范围内的邀请投资的总和;

B. 所有邀请的注册时间满20天的人的投资的总和;

C. 从指定活动注册进来的、完成首投大于1w的人数;

规则的抽象

🔘原子规则条件

前面已经介绍了“基础数据”和“行为数据的规约结果”,基于这两类数据可以构造表达式,诸如%、+、-、*、/、甚至java库中数学运算Math,commons包的DateUtils等任意第三方库都可用于构造表达式,比较操作符诸如>、>=、<、<=、contains、not contains、start with、end with、between等。左右表达式结合比较操作符构成一个原子的规则条件:

在“基础数据”上构建的原子规则条件如:

A. ${用户等级}等于x级;

B. ${注册时间}在<begin, end>之间;

C. ${是否完成实名绑卡}等于true;

D. ${实名绑卡时间}发生在${注册时间}后30天内;

E. ${投资额}大于x元;

F. ${投资团范围}在(a,b,c,d)内;

G. ${用户手机号}以181开头;

H.“当前投资金额”大于“风险评级”的10倍;

在“行为数据的规约结果”上构建的原子规则条件如:

A. ${最近10天平均投资额}大于1w。

B. ${投资大于1w的总次数}大于5次;

C. ${最近30天投资大于2w的总次数}大于3次;

D. ${最近10次投资大于2w的总次数}大于3次;

E. ${当前自然月的首投大于1w的次数}等于1;

F. ${首次投资发生在注册后20天内的次数}等于1;

需要注意的是单条行为数据本身也是一组简单数据,同样可以在其基础上按照基础数据的方式构建规则。

比如想要判断用户是否满足“已实名用户近20天单次投资超过5w的总次数”大于3,此规则可以抽象为先在投资行为记录上应用时间限制(最近20天)筛选、进一步做基础数据筛选(用户已经实名,且投资额大于5w),最后做count计算得出表达式所需的“行为数据规约结果”。

🔘规则

< 规则组成 >

一条完整的规则由多个“原子规则条件”的AND/OR组成,规则通过后执行action集合。

< 优先级规则 >

一条简单的规则由多个原子规则条件组成,规则通过后执行对应的action集合。但有时候需要多条规则,通过其中一条就跳出去执行action集合,且先跑哪个规则要有优先级顺序。以给近期活跃用户发体验金奖励为例,以下几种情况都可以定义为活跃用户:

规则1:一次性投资超过5w、且已经完成风险测评;

规则2:近7天投资次数超过5次;

规则3:近5天跟帖次数超过10次且完成过至少一次投资;

以上几种情况完成任意一种都会发放体验金,但是满足其中一种,其他规则就不能继续执行,比如用户可能规则1和规则2同时满足,但是要求规则1通过后,其他规则就不能再被触发,这就要求规则之间支持优先级、且互斥存在。

< 延迟规则 >

存在这样的场景,一个规则由用户某个行为触发,但需要延迟一定的时间后再执行。比如要判断用户满足投资后20天内未发生过任何退团,则给其发放体验金奖励,或者判断用户注册后30天内没有任何投资,则给其发放代金券鼓励其投资。

这就要求支持延迟执行的规则,用户注册后触发延迟规则,在30天后运行此延迟规则判断用户是否没有任何投资行为。

自建规则引擎如何满足需求

需要什么?

针对规则引擎,需要满足如下几个需求:

1 易于理解、易于编写、且表达能力强的规则描述语言;

2 支持动态构造规则、并立即热部署生效;

3 支持从数据库等外部存储读取规则;

4 支持基础数据、流式数据上构建规则;

5 数据的计算和加载延迟到规则运行过程中,避免不必要的计算,且支持外部服务调用;

技术选型

实现规则引擎的方式有很多,可以采用原生的脚本语言如Groovy、Python、jruby、java等,好处是表达能力强、可动态修改并生效,但很容易让开发人员陷入规则中写业务的困境,后期的维护困难。

另外其毕竟不是为规则引擎而生、要形成规则引擎框架需要大量的工作。而开源的java规则引擎包括Drools、Mandarax、JLisa、JEOPS 、Prova 、OpenRules 、Open Lexicon、SweetRules 、JRuleEngine 、Zilonis 、DTRules 、OpenL Tablets等几十种,它们的好处是为规则引擎而生,已经为此做了大量工作,缺点在于学习成本。

我们决定站在巨人的肩上,选择基于社区活跃、文档丰富、功能强大的Drools来构建规则引擎,其最近一次发布是2018/04/04的7.7.0版。Drools本身能很好地支持支持java表达式、外部依赖数据运行时加载、基于时间的流式数据计算、规则互斥、规则优先级等功能,同时还和动态脚本语言一样可以动态生成、并动态执行,基于Drools构建的规则引擎框架可以很好地满足3.1中提到的所有需求。

如何使用

步骤一:提供数据模型

以lender用户为例,首先定义其数据模型,如下表所示为部分数据模型定义:

(1)基础数据

(2)流式数据

步骤二:构造规则

然后就是基于数据模型构造规则,下面举几个规则的例子,其中规则1-4的数据主要是来自于前面提到的“基础数据”,规则5开始涉及到“流式数据的规约结果”,规则3和规则7演示了运行时加载外部数据:

规则1:手机号181开头的用户、投资金额大于1w且排除特定的投团范围

这里演示了从准备数据,构造规则,到运行规则的整个过程。规则部分由“原子规则条件”组成,然后是设置规则通过后要执行的Action集合,最后运行规则。需要注意的是数据需用${}标记,接下来演示更多规则,将只贴出来具体规则部分的代码。

规则2:投资额为偶数、且投资额大于年龄的30倍和用户等级的100倍中最小的那个值

比较的左右两边都可以支持任意${}数据、以及任意java表达式,包括java.lang包、java.util包等一切可以引入的jar包功能。

规则3:注册时间在指定范围内、已经完成实名、且在注册后的20天内完成绑卡

运行时一旦发现${}数据是传入的数据集合中不存在的,则会尝试加载以该数据变量命名的外部数据加载器计算并加载数据到规则内存中,这里registerTime、registerChannel等数据都是当前传入的数据中没有的,所以会调用对应的数据加载器加载数据到规则内存中,我们需要做的就是针对registerTime、registerChannel编写对应的数据加载器:

规则4:完成实名、或完成绑卡

注意,下面从规则5开始,会演示表达式中的数据来自“流式数据的规约结果”。

规则5:用户大于1w的投资的次数、大于3次

前面的规则都是基于基础数据的比较,而这里是针对流式数据的操作,正如前面介绍的,首先执行reduce操作,包括筛选用户投资记录数据流中投资额超过1w的记录,并执行count操作,然后与condition进行比较,判断次数是否大于三次。

需要注意的是,流式数据也是外部依赖数据,并不是在规则执行前加载到规则内存中的,而是需要运行时动态加载,所以我们需要针对“STREAM_INEST”编写对应的流式数据加载器,根据reduce操作传入的Duration限制(包括时间和长度的限制)返回数据流,流式数据加载器的例子参见规则7。

规则6:用户大于1w的投资的总额、大于5w

规则7:最近7天投资额大于1w的总次数、大于3次

可以对流式数据增加时间窗口限制,其他支持的时间窗口包括最近x秒/分钟/小时/月/年、当前自然周/月/年等。下面是用户投资流式数据加载器实现,所有类型时间限制都会转换成<beginDate、endDate、limit>,加载器根据这三个字段的值查询数据返回。

实现原理

规则引擎执行的入口即前面提到的DrRuleEngine.runRuleEngine(Map,List<DrRule>),大致思路是将DrRule规则集合转换为Drools规则语言并执行,大致分为如下步骤:

如何将DrRule规则集合转换为Drools规则语言,通过Pattern、操作符、java表达式、from、accumulate、salience、activation-group等丰富的Drools功能支撑我们的规则是核心。另外规则的加载到转换为知识库KieBase的过程需要异步化,而不是每次运行规则都实时构建知识库,这样有助于提升性能,后面介绍的基于自建规则引擎构建的促销系统就是这样做的。下面是自建规则引擎运行时从DrRule规则集合动态生成的Drools规则语言片段:

基于自建规则引擎构建促销系统

如下图所示为DR促销系统规则引擎和发奖相关的架构图:

1.规则配置控台

从数据库读取支持的数据模型呈现给运营人员,运营人员基于数据模型,通过UI界面创建规则及对应的奖励,创建好的规则将存储到数据库中。

前面已经提到了lender用户的数据模型,展示到UI提供给运营人员的就是数据模型的中文描述,如用户${注册渠道}、${注册时间}、${投资额}、${投资团等},操作符如大于、大于等于/小于/小于等于/包含于/闭区间/开区间等,运营人员只需在界面上选择对应的数据、操作符、以及创建对应的奖励,即可生成对应规则。

2.规则引擎

来自MQ的用户行为、来自定时Job的调用、或来自第三方系统的http调用都可以触发规则引擎实时加载并执行对应的规则。

3.延迟规则存储和加载模块

针对一些需要延迟触发规则二次执行的特殊场景,在触发行为发生后,将需要延迟调度的规则存储到库中,由延迟规则调度模块在正确的时间触发规则引擎执行。

4.发奖系统

发奖系统只业务落地的核心,当用户满足特定规则后,需要根据配置发放对应奖励,这里面包括优惠券、体验金、现金、DR币等的发放,甚至包含app/短信/微信等不同渠道的用户消息促达。

促销系统的核心不是规则引擎,而是数据、以及运营模式,规则引擎只是工具。如何构造并完善用户数据模型;如何让运营人员快速方便地构建,修改规则,并能够立刻生效;如何让开发人员快速响应新的业务需求开发新的条件和调整已有条件;才是更应该去思考的。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多