登录 |  注册
首页 >  网站架构 >  腾讯技术园 >  亿级 QQ 会员活动运营系统的设计之道

亿级 QQ 会员活动运营系统的设计之道

1. 前言

随着 QQ 会员用户的日益增涨,每周都要上线大量各种玩法的 H5 活动来满足产品和运营的需求。大概在 2014 年,那时手游非常火爆,我们部门有一个游戏特权小组(大概 10 多个人),就是专门每天开发这种游戏相关的 H5 活动的。上线一个活动,从需求评审到设计重构,再到开发,到测试,最后上线。整个流程下来,大概一周左右,效率非常低下,已经无法满足运营快速上线的诉求,并且重复性工作也非常多,对开发同学不能得到好的技术提升。于是,我们整个开发团队开始思考着怎么去提升开发和运营的效率。因此我们需要设计一套能快速上线、扩展性好、可复用性高的运营系统。于是,QQ 会员活动运营系统(以下简称 AMS)应运而生。

这里说的“道”,包含两层意思:一是这套系统设计的过程,二是从这套系统总结出来的方法思路。

2. QQ 会员活动运营系统简介(AMS)

2.1 AMS 简介

AMS 是一个包括重构、开发、测试、发布、运营、监控等全生命周期的活动运营平台。

AMS 最新统计数据大概如下:

  • 每年为部们运营的活动带来的收益为 100+ 亿;

  • 最新峰值PV为 10 亿 +,日均PV为 5 亿;

  • 每天 600+ 个活动在现网运营;

  • 4000 多个运营人员,10 多个业务部门在使用。

下图为 AMS 近 30 天的 PV 请求,10 月 28 日有活动推广,请求量达到峰值 10.61 亿。

qq.png

整个公司内部使用 AMS 的业务部们主要有:QQ 会员、QQ 浏览器、QQ 动漫、QQ 阅读、游戏中心、互娱、QQ 钱包、财付通、鹅漫 U 品等。

qq产品.png

2.2 什么是活动?

1. 活动的定义

简单来说,就是由一系列逻辑规则组合而成的一个 H5 页面。比如下面的截图,都是一个个活动。

2. 这些活动的规则一般比较多(这里的规则你可以理解为参与的条件,我们简称为 Rule),比如下面是我们业务比较常见的条件 Rule:

  • 是否为会员

  • 是否为超级会员

  • XX 游戏的注册时间是否大于 XX 时间

  • XX 游戏的游戏角色等级是否大于 XX 级

  • 最近是否登陆过 XX 游戏

  • 是否有抽奖机会

  • ……

3. 只要用户满足了上面指定的一些条件后,就能获得指定的奖励(一般是虚拟奖励),并给用户发货(这里的奖励发货我们简称为 OP),比如下面是我们业务比较常见的发货:

  • 游戏道具

  • 游戏礼包

  • 会员成长值

  • 1 个月的会员

  • Q 币

  • ……

当然,更多时候,这些活动的条件 Rule 都是 N(N > 1)个组合在一起的。比如,要满足的条件 Rule 为:

  • 条件一,是会员;

  • 条件二,最近登陆过天天酷跑游戏。

要满足上面 2 个条件,用户才能获得一个游戏礼包,那么这里的发货 OP 为:游戏礼包。

想当初,我们要实现上面的逻辑,我们会写出一个大的 API 接口,然后在这个接口里实现上面 2 个条件的判断,如果满足条件,就进行发货操作。这种开发模式持续了一大段时间后,我们发现重复性的工作太多,很多代码冗余,代码也越来越难以维护。为了避免这种问题,于是我们把上面这些条件 Rule 和发货 OP 进行抽象,并封装成一个个 Rule 和 OP 组件。然后,由 AMS API 引擎来进行加载并执行。

那么我们又是如何设计这套 API 引擎的呢?

3. API 引擎的设计

3.1 设计目标

经过自身业务场景的详细分析,我们的 API 引擎的设计目标为:

  • 每个接口请求都有一个唯一的活动号(actid);

  • 接口输入输出结构要统一,这样在前端才能做统一的解析处理;

  • 可灵活装载任意个规则组件(Rule);

  • 可灵活装载一个发货组件(OP)。

3.2 工作原理

根据上面设定的目标,API 引擎的运行流程图大概如下(注:这里进行了简化,实际情况要复杂得多):

捕获.png

每一个请求接口用参数活动号 actid 来标识,先用 actid 查询出相应的配置信息(包括基本信息、Rule 信息、OP 信息),然后读取其中的 Rule 组件信息,并一一执行相应的 Rule 组件。如果其中一个 Rule 不满足条件,则直接返回错误的 JSON;如果全部 Rule 都满足了,则加载发货组件 OP 并执行,最后返回成功的 JSON。下面我再详细说明。

1. 每个接口请求都有一个唯一的活动号

这里的活动号参数,标识了一个唯一的业务逻辑,你也可以理解为接口名。API 引擎会根据当前的 actid 从 CMEM 存储里获取当前 actid 的配置信息(包括所有的 Rule、OP 组件信息)。

假如有个活动的业务场景为:只有会员,且会员等级大于 4 的用户点击参与后可以获得一个虚拟货币。那么这个 actid 对应存储的信息可能如下所示:

 {
       "actid":302620,
       "name":"领取礼包",
       "status":0,
       "start_time":1502726400,
       "end_time":1606780800,
       "rulecfg":{
           "rulecfg":[
               [
                   {
                       "rule":{
                           "name":"club"
                       },
                       "type":"==",
                       "value":"1"
                   },
                   {
                       "rule":{
                           "name":"viplvl"
                       },
                       "type":">",
                       "value":"4"
                   }
               ]
           ],
           "rule":[
               "club==1&viplvl>4"
           ]
       },
       "opcfg":{
           "op":"add_money",
           "plist":{
               "money_id":"302620",
               "num":"1"
           }
       }
   }
  • rulecfg 结构: 表示存储了 Rule 名为 club 和 viplvl 两个组件,type 是运算符,value 是比较的值 。

  • Rule 结构: 表示最终生成的运算规则。

  • opcf 结构: 表示执行的 OP 信息。

2. 接口输入输出结构要统一

还是上面的例子。其对外 API 请求的接口地址为:

http://iyouxi.vip.qq.com/ams3.0.php?c=page&gtk=e4b0b73337ae43e4c62b40528c258b7b&actid=302620

执行成功后的返回 JSON 结果如下:

{
       "ret": 0,
       "data": {
         "act": {
           "start_time": 1502726400,
           "end_time": 1606780800,
           "op": "add_money"
         },
         "rule": {
           "club": "1",
           "viplvl": "4"
         },
         "op": {
           "num": 1
         },
         "actname": "领取礼包"
       },
       "time": "1542180520",
       "actid": 302620,
       "msg": "success"
   }
  • ret 返回码: ret=0 表示用户满足条件,参与成功;反之表示用户不满足条件,参与失败。

  • act 结构体:表示该活动的基本信息,如开始时间,结束时间等。

  • Rule 结构:存储了的 Rule 组件信息。

  • OP 结构体:存储了发货 OP 信息。 统一了这些主要的结构信息,前端的 JS 才能做统一的解析。

3. 灵活装载任意个规则组件 Rule

我们又是如何设计的呢?

首先,我们先了解下规则组件 Rule 是怎么设计的。这里用一个是否为会员的规则 Rule 为例子,这个 Rule 组件的代码大概如下所示:

 /**
    * 是否会员(0:非会员;1:会员;2:会员但非有效)
    *
    */
   class Rule_club extends CellBaseRule implements IfCellRule
   {
       public function run( $rulePregResult = array() ) {      
           $this->runValue = ClubService::isClub($this->uin);
           return Code::SUCCESS;
       }
   }


这里的代码其实很简单,就是判断下当前用户是否为会员。并把结果返回给 runValue,然后交给 API 引擎进行运算。CellBaseRule 类 和 IfCellRule 接口类主要是定义了一些全局性的方法和属性,这里不再详细解说。

主引擎 API 通过获取该活动 actid 的 Rule 配置信息,依次加载相应的规则 Rule 文件,并一一执行相应的 Rule 组件。

4. 装载一个发货组件(OP)

原理其实和 Rule 差不多,唯一不一样的是,OP 只能有一个,而 Rule 可以有多个。这是为什么呢? 主要是因为,如果发货 OP 有多个,一旦某一个 OP 发货失败,我们没有好办法去处理事务回滚的问题。每个 OP 做的事情可能都是不一样的,请求的服务接口也是不一样的。基于这种考虑,系统只允许执行一个发货 OP。不过对我们业务来说,基本也满足了。

3.3 运营管理系统简介

讲到这里,有些人一定会问,我们如何在指定的这个活动号中加载那些 Rule 组件和 OP 组件呢? 答案很简单,我们不光设计了这套 API 引擎,我们还搭建了一套运营管理系统,在这套运营管理系统上可以自由的新建活动号(actid),然后在每个 actid 中配置相应的 Rule 和 OP 组件。

假如我们要开发如下的 H5 活动:

QQ活动.png

那么我们只要在运营管理系统中新建 3 个活动 id。

一个 H5 活动的所有 actid 活动列表,这里配置了 3 个 actid。

有了这套系统,我们开发活动大大节省了人力,比如以上面这个活动 H5 的开发为例,我们从原来传统的开发流程为:

  • 写 3 个 API 接口,并编码实现所有的业务逻辑;

  • H5 图片中的三个按钮,分别请求相应的 API 接口。

演化后的开发流程:

在运营管理系统新建 3 个活动号,并在每个活动号 actid 配置上相应的 Rule 和 OP,如果之前开发了相应的 Rule 和 OP,则直接配置使用;如果是新的 Rule 或 OP,则去扩展开发(实现上到目前为止,AMS 现成的组件 Rule+OP 已接近 2000 个了,大部分的活动基本都可以复用这些组件);

在 H5 中的按钮绑定相应 actid 的 API 请求。

总结下 AMS API 引擎的设计特点,可以归纳为以下 3 点

  • 组件化,对重复的 Rule、OP 进行组件化设计;

  • 配置化,对执行流程中的 Rule、OP 进行可配置化;

  • 自动化,对人工配置的数据进行自动检查其正确性。

通过 AMS 系统一期的设计,大大降低了人力成本,原来一个人可能要一周完成的工作,现在基本 1~3 天就可以完成。开发一个活动的效率是提高了,但要开发的活动却是越来越多。因为现在是从原来的一个人一周开发一个活动,变为一个人一周要开发 2~3 个活动。虽然这里提高了开发的开发效率,但是整个活动的开发流程却没有得到根本性的改变,流程还是比较多,参与的人也比较多,周期也相对较长。能否再次缩短时间呢?于是,经过开发和产品一段时期的反复沟通,AMS 二期开始了。

上面我们说的 API 引擎就是图中的业务层,而运营管理系统和活动模板对应图中的运营平台。

一个要支撑住峰值 10 亿 PV 的系统,光有一套代码层是远远不够的,其最重要的是要高可用。那么 AMS 是如何在高并发的情况下保证系统高可用的呢? 这里我分享 3 点 AMS 系统主要运用的设计原则。

1. 过载保护

主要是做了无效请求拦截、限流、限频、session 锁、安全打击等。

2. 柔性可性

AMS 系统的很多 OP 都是调用第三方的接口,执行时间也比较长,直接影响系统的 QPS。因此我们主要对服务进行降级处理,当有大流量推广时,我们把活动的发货方式由同步发货改为异步发货的方式,减少发货的时延,提高系统的 QPS。

3. 大系统小做

拆分大的系统为一个个小系统,减少子系统间的耦合及相互影响。比如我们在 Web 服务层部署时,部署了多个集群。即使某一个集群挂掉了,也不影响整个系统。就像下面的图一样,哪个系统更稳定,大家一目了然。

4. 小结

以上就是我分享的设计之道。最后再做下个总结,这里说的“道”,包含两层意思:

  • 一是设计过程,好的系统不是一开始就是设计得很好,而是经过了不断演化的过程。

  • 二是方法思路,AMS 主要有组件化、配置化、自动化。这里我再抽象一下,变得更通用一些。

    • 组件化:对重复不变的事组件化

    • 配置化:对变化的事配置化

    • 自动化:能让程序做的事尽量让程序自动化处理

另外系统的架构上主要使用了以下 3 种常用的设计思维:

  • 过载保护

  • 柔性可性

  • 大系统小做

QQ 会员运营系统(AMS)结过多年的不断积累及迭代,其实际情况复杂得多,技术细节也非常之多,本文其实做了很多简化。本文重点在于分享这套系统的设计过程和设计思维,希望对你有所帮助!

本文首发于 GitChat,未经授权不得转载,转载需与 GitChat 联系。 

下一篇: 深入理解 MySQL 索引底层原理
推荐文章
  • 架构师在进行系统设计时,遵循一套复杂且综合的方法论,主要包括以下核心步骤:需求分析:理解并明确业务需求是架构设计的第一步。架构师需要与各利益相关者深入沟通,收集和分析业务需求、性能需求、安全性需求、扩展性需求等。领域建模:基于需求分析,构建抽象的业务模型或数据模型,明确系统的边界、核心实体及其关系。
  • 根据阿里交易型业务的特点,以及在双十一这样业内罕有的需求推动下,我们在官方的MySQL基础上增加了非常多实用的功能、性能补丁。而在使用MySQL的过程中,数据一致性是绕不开的话题之一。本文主要从阿里巴巴“去IOE”的后时代讲起,向大家简单介绍下我们过去几年在MySQL数据一致性上的努力和实践,以及目
  • 随着深度学习在图像、语言、广告点击率预估等各个领域不断发展,很多团队开始探索深度学习技术在业务层面的实践与应用。而在广告CTR预估方面,新模型也是层出不穷:WideandDeep、DeepCrossNetwork、DeepFM、xDeepFM,美团很多篇深度学习博客也做了详细的介绍。但是,当
  • 1.背景搜索优化问题,是个典型的AI应用问题,而AI应用问题首先是个系统问题。经历近10年的技术积累和沉淀,美团搜索系统架构从传统检索引擎升级转变为AI搜索引擎。当前,美团搜索整体架构主要由搜索数据平台、在线检索框架及云搜平台、在线AI服务及实验平台三大体系构成。在AI服务及实验平台中,模型训练平台
  • 行业算法版简介OpenSearch-行业算法版是基于阿里巴巴自主研发的大规模分布式搜索引擎搭建的一站式智能搜索业务开发平台,目前为包括淘宝、天猫在内的阿里集团核心业务提供搜索服务支持。通过内置各行业的查询语义理解、机器学习排序算法等能力,提供充分开放的引擎能力,助力开发者快速搭建智能搜索服务。Ope
  • 一.概述我们在考虑MySQL数据库的高可用的架构时,主要要考虑如下几方面:如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断。用作备份、只读副本等功能的非主节点的数据应该和主节点的数据实时或者最终保持一致。当业务发生数据库切换时
学习大纲
亿级 QQ 会员活动运营系统的设计之道
 深入理解 MySQL 索引底层原理