-
关注Ta
-
- 注册时间 2013-02-23
- 最后登录 2024-12-25
-
- 发帖313595
- 在线16870小时
- 精华0
- DB1043
- 威望28082
- 保证金0
- 桃子20
- 鲜花0
- 鸡蛋0
-
访问TA的空间加好友用道具
|
本篇文章选自作者在 GitChat 的分享,若有什么问题,可在公众号回复「小助手」添加小助手微信,邀请你进入技术交流群。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 亿。整个公司内部使用 AMS 的业务部们主要有:QQ 会员、QQ 浏览器、QQ 动漫、QQ 阅读、游戏中心、互娱、QQ 钱包、财付通、鹅漫 U 品等。如下图所示: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)。
[font="]3.2 工作原理[font="]根据上面设定的目标,API 引擎的运行流程图大概如下(注:这里进行了简化,实际情况要复杂得多):每一个请求接口用参数活动号 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>k=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 活动:那么我们只要在运营管理系统中新建 3 个活动 id。其大致的界面如下:界面 1一个 H5 活动的所有 actid 活动列表,这里配置了 3 个 actid。对应:界面 2活动号 actid=30620 的 Rule 组件配置,这里配置了两个条件,一个条件是会员,另一个条件是会员等级大于 4。界面 3活动号 actid=30620 的 OP 组件配置,这里配置了一个添加虚拟货币的 OP。有了这套系统,我们开发活动大大节省了人力,比如以上面这个活动 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 二期开始了。3.4 AMS 二期的设计(又叫活动模板)1. 目标:30 分钟配置一个 H5 活动这个当时给团队的挑战其实是很大的。经过我们的详细分析,发现有些环节还是可以再次进行组件化、配置化、自动化。于是我们又砍掉了很多人工环节,其整个活动的开发流程原来是这样的:演变后流程为:从上面的 AMS 二期流程图我们可以看到,流程里直接砍掉了重构和开发两个最花时间的重要环节。这意味着,不需要重构和开发来参与了。还是直接上图看下 AMS 二期的设计效果吧。2. 配置步骤现在上线一个活动,完全由设计师和运营人员 2 人搞定,大概的步骤为:- 设计 H5 背景图片(角色:设计师);
- 创建相应活动号,上传背景图,拖拉相应组件,给组件分配相应的活动号 actid(角色:运营或产品);
- 发布测试、上线(角色:运营或产品)。
像上面这个抽奖活动,如果设计师已经设计好背景图的情况下,运营人员只要花个 20 分钟的样子就可以配置好并上线了。3. 工作原理1) 自动切图上传设计师设计好的背景图片,由于整张图下比较大(大部份都是大于 4M)。如果一次性加载一张 4M 的图片,在移动端打开速度是非常慢的。因此,我们把背景图自动切成若干张小图,并把切好的小图自动上传到图片 CDN。2) H5 组件化对 H5 页面中常用到的视图做成一个个 H5 组件,并且可以替换一些基本的元素(如文字,按钮图片,文字大小,颜色等)。3) DOM 结点存储配置数据每个 H5 组件可以绑定一个活动 id,并在该组件的 data-logic 属性里存储相应的配置逻辑。比如,下面的 H5 活动其最终生成的 HTML 大概如下:熟悉 HTML 和 CSS 的开发应该不难理解其中的含义。4) 活动模板的设计特点总结还是这 3 点:- 组件化,对 H5 元素组件化设计;
- 配置化,对 H5 界面可配置化,对组件的外观可配置化;
- 自动化,对上传的背景图片自动化切图,自动化生成 DOM 节点,自动化生成手机访问二维码等。
AMS 活动模板上线后,开发团队再也不用去做这些重复性的活动开发了,而是直接交给产品运营同学去配置了,终于解放了开发。4. 系统架构首先来看下 AMS 结构分层,简单的系统结构如下:上面我们说的 API 引擎就是图中的业务层,而运营管理系统和活动模板对应图中的运营平台。一个要支撑住峰值 10 亿 PV 的系统,光有一套代码层是远远不够的,其最重要的是要高可用。那么 AMS 是如何在高并发的情况下保证系统高可用的呢? 这里我分享 3 点 AMS 系统主要运用的设计原则。1. 过载保护主要是做了无效请求拦截、限流、限频、session 锁、安全打击等。2. 柔性可性AMS 系统的很多 OP 都是调用第三方的接口,执行时间也比较长,直接影响系统的 QPS。因此我们主要对服务进行降级处理,当有大流量推广时,我们把活动的发货方式由同步发货改为异步发货的方式,减少发货的时延,提高系统的 QPS。同步方式流程如下:异步发货方式流程如下,主要是我们使用了 MQ 消息队列来消费。3. 大系统小做拆分大的系统为一个个小系统,减少子系统间的耦合及相互影响。比如我们在 Web 服务层部署时,部署了多个集群。即使某一个集群挂掉了,也不影响整个系统。就像下面的图一样,哪个系统更稳定,大家一目了然。5. 小结以上就是我分享的设计之道。最后再做下个总结,这里说的“道”,包含两层意思: 一是设计过程,好的系统不是一开始就是设计得很好,而是经过了不断演化的过程。 二是方法思路,AMS 主要有组件化、配置化、自动化。这里我再抽象一下,变得更通用一些。- 组件化:对重复不变的事组件化
- 配置化:对变化的事配置化
- 自动化:能让程序做的事尽量让程序自动化处理
另外系统的架构上主要使用了以下 3 种常用的设计思维:QQ 会员运营系统(AMS)结过多年的不断积累及迭代,其实际情况复杂得多,技术细节也非常之多,本文其实做了很多简化。本文重点在于分享这套系统的设计过程和设计思维,希望对你有所帮助!本文首发于 GitChat,未经授权不得
|