一次使用规则引擎改造任务系统的经验
–
作者:爱学习的码农靖
链接:juejin.cn/post/7401403660246614070
笔者在去年接手了公司的活动中台,其中【任务】,是活动当中必不可缺的玩法之一。
相信大家都在各种各样的互联网活动中体验过【任务】。
比如:
- 打车软件去完成一笔订单可以给用户发一个奖品;
- 如用户去申请白条的额度,就能给用户发放京豆;
从技术思维上,以上的流程,可以抽象成,用户完成某个动作,就给用户发放对应的奖品。
作为中台的任务系统,往往会对接上游N个业务方的不同任务,并且任务也会有不同的完成条件。
如:
电商业务:用户支付一笔3000元以上的订单,完成【订单任务】
商业化业务:用户完成一个app下载,且打开app浏览10秒,完成【app下载任务】
生活业务:用户完成一笔话费充值,且充值金额大于50元,完成【话费充值业务】
具体的流程图如下:
可以看到,业务侧将消息通过RocketMQ的方式,通知至任务系统。
如何避免对接新的任务,改造代码
以上方式,会有一个实实在在的痛点:每次任务完成条件发生变更,就需要任务系统更改代码,现状需要一周的时间去对接一个新的任务。
举个例子,假如电商业务中,【订单任务】的完成条件从【3000元以上订单】调整为【5000元以上订单】,任务系统每次还得去调整代码,以支持【5000元以上订单】完成的条件。这种如此慢的迭代方式,显然是不够支持业务的发展速度。
所以任务系统的行动点就是:任务完成条件变动,做成可配置化,支持运营在后台管理系统随时调整任务的配置条件。
1、统一消息格式
其实仔细分析,任务完全可以抽象成为:针对某个行为, 判断行为中的条件是否满足运营配置的要求
但是系统的现状为,各个业务的消息体虽都是json格式,但格式都不一致,任务系统写了很多针对不同消息格式的定制化代码适配,其流程图如下:
因此我先要求业务方的mq格式消息体进行改造,按照任务系统的统一标准进行格式传递。
标准消息体格式如下
"userid": "",
"business": "",
"behavior": "" ,
"behaviorTime": "",
"extraData": {
}
userId是用户的id,business是该事件所属业务,behaviorTime是行为发生的时间, behavior就是用户具体的任务行为,比如【完成订单叫pay_order_success】, app下载叫【app_download】, 话费充值叫【tel_recharge】。
最为关键的是extraData, 这个字段是拓展字段,主要用于存放每个行为的附加条件,比如:
【完成订单】,该字段可以存放订单交易金额,是否首次下单,那么完成订单的完整消息为:
"userid": "12345",
"business": "onlineShop",
"behavior": "pay_order_success" ,
"behaviorTime": "完成订单时间戳",
"extraData": {
"tradeTotalMoney": 500000,
"firstTime": true
}
以上完成消息对应的中文含义为:用户12345, 在某个时间,完成了【电商业务】中的【下单成功】行为,行为的附加属性为:【交易金额大于5000元,用户本次是首次交易】。
之后,所有对接任务系统的任务,业务方必须按照我的要求,来传递消息。
做好了任务消息的标准化,就开始下一步优化:如何将完成条件进行抽象,做成可配置化管理?
2、完成条件可配置化
想把任务完成的配置完全交给运营,首先需要有个类似下面这种配置的管理后台,为简单的画一下管理后台的原型。
灵魂画家。。。总而言之,就是把任务的完成条件开放至运营,并支持可配。
根据我从业经验,这种规则支持配置的场景,便是【规则引擎】的最佳使用场景。结合项目组的现状,我决定使用阿里的QLExpress, 并进行重构。
QLEpress介绍如下:
https://github.com/alibaba/QLExpress
由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。
在阿里集团有很强的影响力,同时为了自身不断优化、发扬开源贡献精神,于2012年开源
引入了规则引擎后,在判断任务完成的代码时候,其实就是对QlExpress的脚本进行判断,任务的完成条件可以表现为一串脚本命令,如:
tradeTotalMoney >= 300000 && firstTime = true
在执行完成任务代码的时候,任务系统会将【任务消息体】中的extraData,构造成一个HashMap, 传入到QLEpress的api中。
"userid": "12345",
"business": "onlineShop",
"behavior": "pay_order_success" ,
"behaviorTime": "完成订单时间戳",
"extraData": {
"tradeTotalMoney": 500000,
"firstTime": true
}
使用QLEpress的api,express就是【脚本命令】, context是一个Map,对于任务系统来说,就会把extraData传入函数中。
Object r = runner.execute(express, context, null, true, false);
对于QLEpress来说也很简单,就是做了Map对象中的key替换的动作,实际上就是执行以下的这段代码:
Object r = 500000 >= 300000 && true = true
其系统流程图如下(图2.1):
假如运营现在想把任务系统中【完成订单】的完成条件中的订单交易金额5000改成3000,也很简单了,只需通过后台管理系统更改配置就能实现任务的调整。
3、后台系统新增条件的可配置化。
你以为以上就是最终方案了么,对接新的任务就不用开发了么?
其实不然,细心的读者会发现,后台系统如果要新增 【任务完成行为】 和【完成条件】,后台管理系统还是得开发代码。比如:需要新增一个【加入购物车】的任务,还是需要后台系统开发。
所以,我们的下一步优化策略,是将【任务完成行为】和 【完成条件】做成配置化,后台动态读取数据库的配置进行显示,其流程如下:
这里我把属于【规则引擎】的内容抽象成一个系统,让这个系统去对规则领域的内容进行管理。
其实12步,“引入【规则引擎系统提供的sdk】,执行规则引擎代码”,有两种方案,也可以由任务系统在交互的过程中,传入参数,去调用【规则引擎系统】提供的RPC接口,但考虑到增加了一层网络开销,且规则引擎执行的场景也相对简单,不会对规则引擎的代码进行过多调整,所以采取的是引入sdk的方式,通过本地代码执行去减少不必要的网络开销。
结论
经过规则引擎的重构后,再对接新的任务,只需通过开发1分钟在后台配置元数据,即可完成新的任务对接,比起之前动辄一周的新任务对接,大大提高了效率。
笔者也通过这次改造获得了公司的好绩效,并将这种规则引擎的能力,提供给其它系统使用,真正实现了开发驱动业务。