作者:闲鱼技术-兰昊
我们最先落地的业务是在用户增长上,闲鱼的用户增长业务有如下描述:
在年初时,我们在用户增长下做了多个实验,其中两个实验如下:
之所以会做以上实验,主要还是希望用户能在APP上多停留一会。当用户浏览时间越长,就越有可能发现闲鱼上还有很多有趣的内容,无论是商品宝贝还是鱼塘内的帖子。从而达到吸引用户下一次还能再回来的目的,最终带来用户增长。我们做的实验上线后大部分都取得了不错的业务效果,但是在过程中也暴露了两个问题:
针对上述问题,我们先做了一层业务抽象。运营先通过对用户的各种行为进行一个分析和归类,得出一个共同的具体的规则,再将这个规则实时地作用到用户身上进行干预。
针对这层业务抽象,我们再做了工程化,目的就是为了提升研发效率和运营效率。这样就有了第一个方案——基于事件流的规则引擎,我们认为用户的行为是一串顺序的行为事件流,使用一段简单的事件描述DSL,再结合输入和输出的定义,就可以完整地定义一个规则。
以上述用户增长的第二个实验为例,如下图所示的DSL即可简单表达出来:
该规则引擎可以很好地解决之前用户增长业务下的几个策略,随后我们进行了内部推广,准备在闲鱼C2C安全业务下也落地。在C2C安全业务上有如下描述:
在C2C安全业务上,也有一个看似是一个针对一系列行为作出的规则抽象,如下图所示:
但是将上述规则套上规则引擎后,就会发现无法将安全的规则套上规则引擎。假设我们的详细规则是1分钟内被拉黑2次,就对该用户打上高危标记。那么我们想一想,当来了第一个拉黑事件后,匹配上了。然后紧接着来了第二个拉黑事件,也匹配上了。此时按照规则引擎的视角,条件已经满足了,可以进行下一步操作了。但是再仔细看一看规则,我们的规则是要被不同的用户拉黑,因为有可能是同一个用户操作了多次拉黑(同时多开设备)。而规则引擎上只知道匹配到了2次拉黑事件,对规则引擎来说已经满足了。却无法知道是否是不同人操作的。起根本原因是因为在规则引擎里,事件都是无状态的,无法回溯去做聚合计算。
针对规则引擎的局限性,重新分析和梳理了我们的实际业务场景。并结合了业界知名的通用的解决方案后,设计出了新的方案,定义了新的DSL。可以看到,我们的语法是类SQL的,主要有以下几个考虑:
新的DSL方案与之前的规则引擎相比主要有以下几个增强:
针对之前的C2C业务上的规则描述问题,使用新方案的例子如下:
基于这套用EPL(Event Programming Language)写出的DSL,为了做好工程化,我们做了如下的整体分层架构。为了快速实现最小闭环验证效果,我们选择先基于Blink(Blink是阿里对Flink的内部优化和升级)做云上的解析和计算引擎。
在这个分层架构里,至上而下分别是:
通过切面的方式拦截所有的网络请求和行为打点,再记录到服务端日志流里。同时通过一个事实任务对事件流进行清洗,按前面定义的格式清洗出我们想要的事件。再将清洗后的日志输出到另一个日志流里,供EPL引擎来读取。
由于我们采取了类SQL语法,而Calcite是业界通用的解析SQL的工具,因此我们采用Calcite并通过自定义其中的parser来解析。如果是单一事件的DSL,则会解析成Flink SQL。如果是多事件的DSL,则会解析后通过直接调用Blink的API接口的方式来实现。
当EPL引擎计算出结果之后,会输出给用户触达模块。首先会进行一个Action路由,最终决策出需要由具体哪一个Action来响应,最后通过与客户端之间的长连接将Action下发到端上。端上收到具体的Action后,会先判断当前用户的行为是否允许展示该Action。如果可以的话,就直接执行Action的具体内容,曝光给用户。用户看到这次响应后会有相应的行为,那么这部分的行为会影响到Action路由,对这次的路由的做出一个反馈。
新方案上线后,我们就在越来越多的业务场景里进行了落地。这里列举2个例子:
在上述鱼塘的例子里,可以看出来,我们这套方案已经有了一点算法推荐的影子了。在上述租房的例子里,由于规则过于复杂,用DSL表达起来很麻烦,所以就做成只采集4次浏览不同租房宝贝的规则,即触发后,就将这里的数据都给到租房的具体开发的业务方,这也是我们在落地过程中摸到的边界。
使用这一套完整方案,研发效率上有了很大的提升。原先通过写代码case by case的方式一般要4个工作日完成整个研发流程,极端情况下需要跟客户端版本则需要2-3周的时间。现在通过写SQL的方式一般要0.5个工作日即可上线。此外,这套方案还有如下几个优势:
通过在多个业务的落地实践,我们也摸索出来这套方案的适用边界:
当前整套方案还有如下几个问题:
因此综上,我们未来的规划将会聚焦于端侧实时计算能力的挖掘和算法能力的结合上。
(en)