伴鱼技术团队

Technology changes the world

伴鱼开放平台 上线了! 源于实践的解决方案,助力企业成就未来!

任务系统:活动组件化实践初探

前言

随着伴鱼业务和用户的快速增长,公司运营活动的形式也越来越丰富,从活动的大类上总结有(包括但不限于):团购活动、排行榜&拉票活动、红包雨活动、预售秒杀活动、抽奖活动等,这些运营活动对于提升转化、用户留存和高用户活跃度等会起到重要作用,因而运营活动占用了增长部门非常多的研发资源。

其中,形如:“用户 A 完成了 W 动作发放 X 奖励(或者触发某种逻辑)”这样形式的需求会比较常见。在初期承接的过程中,各自活动独立开发,有许多重复的开发量,并且即使是重复的部分仍然需要测试同学的介入,各方都有很大的资源投入,并且还会导致整体流程会很长,通常一个活动开发联调和测试时间需要两周左右。所以,高效可复用地进行运营活动是 ROI 非常高的一件事情。

初探

前言中提到了活动中经常出现形如:“用户 A 完成了 W 动作发放 X 奖励”这样形式的需求,如下图所示,我们将满足这种语法的需求定义为一个任务:

stage

从图中,可以看到总会出现相似的部分,例如用户每日录绘本发放不同的奖励、用户邀请购买发放不同的奖励、用户自主签到等。也有很多不同的部分,例如每次的奖励不尽相同、可以完成的动作周期不同等,并且可能对接很多的上游服务。为了快速实现配置化,我们从这些需求中整理出了初版任务系统的目标:

  • 不关心具体动作,以数字简单区分动作。例如活动 A 中 1 指定代表读绘本、活动 B 中 2 指定代表邀请购买;
  • 配置基础的时间规则;
  • 储存任务的相关状态和数据;
  • 记录奖励发放状态,但是发放的内容由业务自己维护;

流程图

如图所示,在初版系统中,业务方需要完成:

  • 自行绑定数字和动作的关系;
  • 业务方通过消费 kafka 队列或者主动查询获取动作指标之后传递给系统;
  • 发放奖励相关逻辑;

但是,经过「绘本 618」的两期活动的洗礼后,我们发现这种方式虽然是使用了配置化的形式,却并没有达成最初的目标,原因有如下几点:

  • 动作和数字的绑定关系有很大的理解成本,且配置风险较高;
  • 维护消费队列和活动任务的成本和风险较高;
  • 重复的任务逻辑,测试同学的时间成本没有减少,与一开始相比,外在表现并没有特别多的减少开发成本和测试成本。

任务系统

在调研了亿级 QQ 会员活动运营系统的设计之道,并结合了游戏中各式各样的任务形式,与伴鱼运营活动对比之后,我们重新梳理出伴鱼任务系统需要支持的核心功能点:

  • 由系统来理解任务达标动作,减少业务方的理解成本;
  • 时间规则支持,例如:每日可完成2次、每周可完成3次、整个活动只能完成一次等类似的规则;
  • 用户主动领取和系统自动发放两种奖励形式;
  • 任务基础状态的维护;
  • 支持任务重置的能力;
  • 支持配置展示内容的能力;
  • 相关数据记录;

针对于上面的需求,我们做了如下的抽象:

任务状态流转

为任务定义如下几个状态:未开始—>未完成—>待领取—>已领取—>已结束,具体状态流转如下图:

image-20210331214625941

动作校验

主要功能是判断上图中 未完成 —> 待领取 阶段用户是否完成了对应动作。为了简化和便于理解,我们调研并选中了第三方的规则校验包 expr ,将用户复杂的动作整理为固定的 kv 对,通过 expr 包判断是否达标。例如,我们定义用户购买绘本 vip 这个动作为:{buy_pb_vip: true},用户购买的不同类型 vip 定义为:{vip_type: forever},{vip_type: year} 等。如果这时有个任务达标动作为:“用户购买绘本 vip 且购买的是永久 vip ”,那么我们就可以这样配置达标规则: buy_pb_vip == true && vip_type == “forever” 。

消费并清洗上游队列

每个动作都对应各自的一些指标。那么如果任务系统需要理解具体指标内容的话,必须有一个角色为系统输入这些信息。此处我们考虑了两个方案:(1)由指标的生产者来构建内容,然后通知到系统。(2)系统主动对接各个消息队列,进行整体的内容清洗和理解。

方案(1)对系统的要求会简单一些,业务数据是由上层构建的。但是这个方案对于业务代码入侵比较严重,并且从数据生产和使用的流程上来说不是特别顺畅。因此我们最终选定了方案(2),以消息队列作为系统的输入变量,认为每个动作通知都是通过消息队列递交给系统。

整体处理流程包括:接收队列消息 -> 内容清洗 -> 系统使用。例如:绘本购买 vip 队列消息。消息中会包含用户的购买时间,购买的 vip 类型等内容。我们将它清洗成为系统可以理解的 kv 对,然后让动作校验部分去使用。如下图所示,我们对接了各个业务方的上游队列,当有新的达标动作需求时,我们只需要新增对接的上游队列,并更新动作校验部分 kv 对即可。对于系统主流程来说,这个更新无感知,且测试同学的介入可以控制在新增消息队列的对接上。

image-20210402175411947

配置项划分模块

我们整理了所有配置项,并根据不同的相关功能将其分成了下图中六类配置组:基础配置,规则配置,奖励配置,展示配置,库存配置,路由配置。

image-20210401160156873

经过以上的抽象后,我们的任务系统可以提供下面的能力支持:

  • 业务方可以优雅对接,不需要额外的理解和冗余开发;
  • 新动作可以快速支持,不影响主系统;
  • 系统打通了活动营销平台的基础活动配置中心,发奖中心,灵活组件服务等模块化服务;
  • 支持正式环境与测试环境一键导入导出;

进一步升级

在承接活动的过程中又渐渐地发现了新的问题:

  • 初始设计中,期望系统能够聚合概念,沉淀一些常用指标作为复选项。例如:

    • 规则配置:任务周期(每日)+ 可重复次数(1次)+ 可由前端手动完成(是)= 每日可被手动完成一次;任务周期(每周)+ 可重复次数(10次)+ 可由前端手动完成(否)= 每周可被自动完成10次
    • 库存配置:用户可完成(不限次) + 任务可被完成(不限次) = 不限库存
    • 路由配置:路由跳转(读绘本页)= 完成读绘本任务;路由跳转(绘本vip售卖页)= 完成绘本 vip 售卖任务

      希望用户能够脱离具体的指标概念,减少理解成本,相应的数据库设计也是同样的思路和方向。但是在实际使用中,概念的聚合和沉淀并没有达到设计的目标。通常来讲:一方面概念的理解并没有那么复杂,聚合操作在实际使用中属于可有可无的状态;另一方面我们可以认为每个任务都是独立存在的,没有可复选的内容。最终实现方案导致在实际使用中配置起来极为复杂,不仅没有达成预想的目标,还增加了额外的维护成本。

  • 系统支持的粒度仍然不够。我们遇到了一类新的需求,如下图。这里包含两个方向的要求:对于任务侧,用户不论何时注册,都有机会参与此活动,任务应该是长时间有效的;对于用户侧,注册后不同任务的可完成时间区间均不相同,那么如何区分这两个方向不同的时间要求呢?这给我们的系统提出了新的挑战。

    123

  • 展示配置的配置项不足。除开基础的展示内容,对于特殊的跳转或者任务交互,前端同学仍然需要独立开发这部分代码,没有节省前端同学实际的开发量。按钮的配置化内容不足以定义前端多样化的动作。

  • 系统各层级响应时间,稳定性需要优化。具体表现例如:清洗后的队列消息堆积,获取任务列表时延影响用户体验

  • 奖励配置边界模糊。除开基础的奖励发放,还支持了对于业务方的回调逻辑,系统无法脱离随业务的开发任务。这部分的系统边界不够清晰。

针对于以上问题,我们进行了新版本的开发,分别给出了如下的答案:

模块重新划分

如下图所示,通过 taskid 这个任务的唯一标识串联所有配置模块。

  • 基础配置回答了一个任务的基本定义;
  • 下一跳(奖励)配置回答了用户达标后到底做什么,并且给出了清晰地边界;
  • 按钮配置用于下发配置给前端,结合波塞冬或者营销活动 sdk ,实现快速开发;
  • 展示配置回答了任务栏展示的整体逻辑和内容;

image-20210401210153531

定义用户任务箱/用户限时任务

用户任务箱:我们可以理解为每个用户在每个活动下都有一个类似收件箱的逻辑结构,当我们想让用户完成任务A的时候,就给他对应活动的收件箱中发布这个任务,此时只有这个用户可以看到并且完成该任务A。特殊的,我们又为被下发的这条记录额外定义了属于它自己的限时时间区间,至此我们就完整的解答了前文中粒度不足的问题。

为了与之前的普通划清边界,我们特殊的定义了一种任务类型为:可下发任务,代表该任务只能在某些节点发给用户来让用户完成。简单流程如下图所示,由业务方为用户下发限时任务,并定义用户的限时完成时间区间,由系统维护内部任务逻辑。对于业务方来说,类似的需求只需要关心三个部分:限时任务什么时候发给用户;用户的限时区间是多久;限时任务的列表信息从哪里获取,回答了这三个问题,就能无痛并且快速接入任务系统能力。

image-20210401211410926

响应优化

  • 增加多级缓存,redis缓存和内存缓存;
  • 优化清洗队列 ;
  • 并发输出;

系统架构设计

任务系统架构设计

整体系统分为以下三个层次:

业务层

业务层提供系统输出能力。包括对内部服务和对客户端的输出能力

系统层

  • 业务处理:封装业务数据用作输出;发放达标奖励;其他状态变更输出到消息队列;
  • 动作达标处理:动作规则校验后,按照当前的库存和周期等边界条件判断是否是系统接收的有效合法动作;
  • 内部数据维护:维护配置数据,用户指标,任务状态变更历史,各级缓存处理和维护;

基础层

系统依赖的一些常用第三方服务和能力:包括消息队列,发奖中心,基础活动服务,常用组件服务,阿波罗动态配置等

通过这次升级,任务系统的概念得到了丰富和扩大,此处的“任务”,其本质已经成为了一个触发器。因此这个版本我们命名为了 trigger 版本。
触发器

研发效率比较

  • 没有任务系统:单个活动开发 + 联调 > 7天;
  • 任务系统 test 版本:单个活动开发 + 联调 = 6天;
  • 任务系统 v1 版本:配置 + 对接新动作 = 2~3天;
  • 任务系统 trigger 版本:配置 + 对接新动作 = 1~2天;

从上面可以看出,随着任务系统的迭代,极大地减轻了运营活动的开发量。

后续工作

  • 清洗队列的 kv 对和系统内部的用户常用属性定义和取值可以在后期直接对接—— 用户画像系统中的业务指标和属性指标;
  • 消息队列清洗后再消费的流程仍然存在更加优雅的实现方式;
  • 支持打通波塞冬前端页面配置化开发;
  • 动作触发为主的需求和系统可以参考这套处理流程;

参考文献

欢迎关注我的其它发布渠道