前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从技术角度谈一谈,我参与设计开发的手Q春节红包项目

从技术角度谈一谈,我参与设计开发的手Q春节红包项目

作者头像
Java高级架构
发布2018-08-16 11:07:27
9550
发布2018-08-16 11:07:27
举报
文章被收录于专栏:JAVA高级架构JAVA高级架构

今年春节期间,QQ以AR技术为支撑、娱乐体验为导向在春节期间推出系列红包并成功刷屏,系列红包包括三大玩法+年初一彩蛋,分别是“LBS+AR天降红包”、刷一刷红包和“面对面”红包,加上“娱乐红包”(明星刷脸红包),共计在春节期间派发了2.5亿现金红包和价值30亿的卡券礼包。根据企鹅智酷提供的数据,手机QQ的用户渗透率在全平台排名第二,为52.9%(第一是微信)。本文将会详细介绍手Q春节红包项目的设计、容灾、运维、架构以及总结。

先来介绍下整个过程中,手Q红包的玩法,以方便不了解的同学理解。2017年的手Q春节游戏红包共有刷一刷/AR地图/扫福三种,如下图所示:

虽然红包分三种,但在游戏业务侧这边的体验都是一样:用户得到一个红包卡券,打开后展示一个(刷一刷红包)或者多个(AR地图红包)游戏的礼包列表,用户选择一个礼包后弹出区服组件,用户确认对应的区服角色信息后会礼包会在48个小时内发放到账。体验如下:

游戏红包的设计容量为入口卡券页流量80k/s,以上体验流程一共涉及三个后台接口:

  • 礼包列表:用户界面的礼包内容需要根据后台接口返回礼包列表进行排序和过滤展示。
  • 区服选择:用户界面弹出的区服组件需要后台接口返回用户区服角色信息。
  • 领取礼包:用户点击“确认”按钮领取礼包,后台进行游戏道具发货。

需求分析

礼包列表

这个功能使用现有能力比较容易解决。活动共有十种游戏,每个游戏有两种礼包:拉新(面向非注册用户,价值80元)/拉活跃(面向注册用户,价值20元),一个用户只能获得这两种礼包中的一种,产品策略允许拉新的用户获得价值较低的拉活跃礼包,反之则不允许。页面展示按用户偏好排序十个游戏,每个游戏展示一个拉新礼包或者一个拉活跃礼包。

出于降低除夕当前流量负载和柔性考虑,在红包活动前,十种游戏的礼包内容作为前端静态数据已经预先通过离线包/CDN下发;红包活动时,后台接口根据用户偏好返回的游戏礼包列表,只是提供前端礼包内容进行过滤和排序,失败了也有前端默认的游戏礼包列表,保障用户体验。

区服信息

这个功能是现有能力。这个角色信息的来源是IDIP,但由于该接口较缓慢(2s左右)且容量较低(低于10k/s),故后台做了一层缓存,将IDIP的区服信息永久性缓存到CMEM中,前台也有本地缓存,在实践中,前台缓存命中率为60%,后台为35%,多级缓存后走到IDIP的请求量只有5%,对IDIP影响不大,只需要扩容现有的区服server和CMEM即可。

领取礼包

这个功能使用现有能力解决存在困难。游戏中心日常发货的道具和平台比较多,平台分为IEG-AMS/MP两种,MP发货对于发游戏道具和发Q币又是两种接口,故我们在架构上使用Facade模式,使用AMS作为发货proxy,屏蔽了底层发货的复杂性,向游戏中心提供统一的发货接口,但比较遗憾的是从AMS到游戏的发货接口都是同步接口,发货能力较低,发货能力最高的王者荣耀也只承诺了3k/s的发货速度,明显不足以直接承受100k/s级别的红包发货,故这里的核心问题是需要有一个队列来解决生产/消费速度不对等的问题。

去年的红包是后台收到发货请求后落地到本地文件返回用户成功,再由一个本机的daemon跑落地文件按游戏方所能提供的发货速度进行实际发货,相当于使用本地队列缓冲。但这个方案存在某台机器挂掉后如果不能恢复会丢失一部分本地的发货数据造成漏发,以及每个高并发业务都要重新做这一套东西不方便通用的问题。

从架构上思考,其实最合理的方案是作为发货proxy的AMS提供异步发货的能力,将用来解决生成/消费速度不匹配的MQ做在AMS内部,为业务提供通用的异步发货能力,业务侧就不需要考虑发货超过游戏方能力的问题,新业务有类似的场景也不需要重新开发。

整体方案与项目分解

整体方案图如上图所示,由于整个项目涉及多方开发,而且模块较多,整个模块的开发周期较长,作为一期开发的话无法跟上基础侧卡券的验收和安排的几次演习/压测,故按“大系统小做”的原则,根据模块的重要和紧急程度分为几期迭代完成,每一期有独立的里程碑目标并达到对应的验收/演习/压测要求:

  • 第一期(方案图左侧部分)为功能需求,在12月9号上线通过卡券方面功能验收,先使用当前的同步发货接口,对性能无特别要求。

需求开发

功能需求开发

核心问题:不同场景的数据一致性

为用户推荐礼包,用户领取时需要经过{4.1AMS外网发货新OP}校验领取资格,后台的推荐数据必须能和AMS的资格校验数据能够对上,否则会出现后台推荐的礼包用户领取时却通不过AMS的资格校验导致领取不了的问题。

接口处理的是单个游戏的领取礼包的请求,资格校验操作判断一个用户是否注册了某个游戏。这个是AMS现有的通用功能,数据存储在AMS的CMEM中,简化描述就是一个key-value模型,key为uin+appid,value如果有注册则为1,没有则为0(实际为了节省存储空间,使用bitmap桶实现,具体参见号码包系统使用文档),导入的数据源是产品在除夕前一周提供10款游戏的全量注册号码包,每个游戏一个文件,文件内容是注册用户的QQ号。

但{3.1后台礼包推荐接口}接口返回的是多个游戏的礼包列表,需要获取十个游戏的用户注册状态。如果读取AMS现有的接口/存储,会有两个问题:

  • AMS号码包服务也要承受等同于推荐列表接口48k/s的流量,需要进行扩容
  • AMS号码包服务调用CMEM虽然可以一次请求合并10个key进行批量读取,但请求到了CMEM的Access机还是要读取多个Cache块,性能并不如单请求单key读取。

解决方案:同质异构的数据冗余

性能需求开发

核心问题:用户领取礼包流量远超游戏发货能力

解决方案:发货异步化

使用一个缓冲队列来解决生产消费能力不对等的问题。用户领取请求到达AMS进行基础的资格校验后将请求放入MQ中,返回用户成功并告知会在48小时内到账。再由后台发货Daemon从MQ中读取请求,通过限速组件控制保证以不超过游戏方发货能力的速率进行发货操作。使用的MQ是部门近来建设的RocketMQ,具体参见会员消息队列(RocketMQ)接入指南。

容错需求开发

核心问题:安全发货

三场活动发放的礼包总数预计将近4亿,如何保障这些礼包对于合法用户能都发货到账,不少发也不多发?如何防范高价值道具被恶意用户刷走?有没有可能内部开发人员自己调用接口给自己发礼包?

解决方案:对账补送/订单号/安全打击/权限控制

1. 订单号解决不多发的问题

用户领取礼包的接口{4.1AMS外网发货新OP}调用成功,会为这个请求附带一个UUID生成的一个全局唯一的订单号,再放进MQ中,{4.3AMS内网发货OP}从MQ中取出消息,调用游戏方发货接口前都会先校验这个订单号是否用过,没用过则将订单号以key的形式写入CMEM,再进行发货操作。如果出现对同一个发货消息进行重复发货,则会发现订单号已经用过了不会进行实际的发货操作,保证以订单号为标识的同一个发货请求只会进行一次发货操作。

2. 对账补送解决不少发的问题

对账:用户领取礼包调用的接口{4.1AMS外网发货新OP}成功写应发流水,{4.3AMS内网发货OP}调用游戏方发货接口的写实发流水,由于部分消息会堆积在消息队列中,这部分称为队列堆积流水。故实际要进行补发操作的流水由以下公式可得:

失败补发流水= 应发流水 - 实发流水 - 队列堆积流水。

由于订单号的存在,可以保证同一个发货请求重复发送也不会多发,对队列中堆积的消息提前进行补发操作也不会导致多发。故当队列中堆积的流水较少的时候,采用应发流水与实发流水的差集作为失败补发流水是合理,只是每个对账周期会对队列中堆积的消息进行两次发货操作,对性能略有损耗。

后台每个小时运行一次增量对账功能,检测MQ消息堆积量量低于某个阈值,则进行对账操作,截取上次对账到此时的应发流水/实发流水,两者相减得到补发流水。

补送:对对账操作得到的补发流水调用游戏方发货接口进行发货补送操作。

3. 安全打击解决高价值道具防刷的问题

对于领奖的请求,都要求都要求带上登录态,对用户进行身份验证,同时对于高价值的道具开启安全打击,上报安全中心进行恶意用户校验,防止被恶意用户刷走。

4. 权限控制解决内部人员监守自盗的问题

对于发货的机器都要安装铁将军,用户需要使用RTX名和token才能登录机器,审计用户在机器上的操作行为;

发货模块对于调用方是需要严格授权,调用方需要申请key,包含程序路径、程序MD5、部署模块等信息,保证发货功能不被随意调用。

监控需求开发

核心问题:红包涉及多个系统的自有监控,数据收集困难

在监控方面有两个主要诉求:

  1. 我们对外提供的服务是否正常?如果有问题,如何快速地发现问题、分析问题?
  2. 实时知道用户在整个系统的行为漏斗模型,每一步的转化率是多少?

游戏红包涉及红包基础侧/业务前台/业务后台/AMS/MQ平台等多个合作方,各个系统有自己的监控系统,数据来源不一致,活动当天一个系统一个系统地收集的话效率太低。

解决方案:汇总各个系统的关键数据到一个视图

红包作为一个涉及多个子系统的聚合系统,我们需要一个汇总了各个子系统关键数据的整体视图,才能够较全面地监控业务核心指标,对系统和业务有较全面把控,避免在监控系统中跳转检索而耗费有限的时间,为迅速响应解决问题提供保证。

接口封装:虽然红包涉及的多个子系统,各自有各自的上报方式和监控系统,但是对于关键数据大都有提供HTTP形式的查询接口,我们通过封装,将接口的定义统一为key-value形式,以(监控项id,开始时间,结束时间)为key,value为(开始时间,结束时间)段内监控id的值之和。

配置化:一场红包活动的监控,可以由一个时间段加若干个监控项定义。比如刷一刷红包,时间段为除夕当天20:00~20:30,监控项为若干页面的点击量,若干礼包的发放量,若干后台接口的请求量,若干MQ的堆积量等等。

通过对接口的封装和配置化,新增一场红包活动,只需要增加一个时间段和若干个监控项的配置文件,比如下图的AR/刷一刷混场/刷一刷专场就是通过3个配置文件定义3场活动,新增一场活动也只需要增加一个配置文件,并可以在一个视图上灵活切换,相当方便。

从上图中我们就可以实时看到实发和应发是大致相等的,队列没有出现堆积,用户在各级页面的转化率,可以很方便地判断系统的健康状态和分析定位问题。

系统保障

第四部分讲述了业务需求的开发,但是否功能开发完成后我们就这样就可放到线上安心睡大觉了呢?

  • 如果出现一部分机器乃至整个机房挂了,服务是否可用?
  • 外部的服务突然故障了,比如MQ突然挂了,不能写入消息了,服务是否可用?
  • 说是红包入口流量8W/s,万一来了20W/s呢?系统会不会挂掉?服务是否可用?

以下从系统容灾/过载保护/柔性可用/立体监控来讲我们这方面做的一些工作,我们对于除夕当天出现各种问题系统还能正常运行,用户能正常体验服务的信心从何而来?

系统容灾

容灾就是在整体服务一部分出现异常时,另一部分能顶上,不影响整体服务的使用,保障业务可用、数据安全。但容灾设计会使得系统复杂度增加,成本和代价也增加,需要额外的开销和资源,应该在合理的范围内考虑容灾。

容灾级别一般划分为多机容灾、多机房容灾,多地容灾,红包的后台服务主要使用公用组件的均衡负载和系统容灾能力,服务无单点问题,采用同地多机房部署,按多机房容灾标准设计。

接入层

典型的GSLB+TGW+QZHTTP接入,GSLB解析域名把请求带到离用户最近的IDC的TGW接入机,TGW再通过内网专线,把请求转发到实际提供WEB服务的QZHTTP服务器上。

  • GSLB:Global Server Load Balance的首字母缩写,意为全局负载均衡,主要提供提供域名解析的就近接入和流量调度。实现在广域网(包括互联网)上不同地域的服务器间的流量调配,保证使用最佳的离自己最近的客户服务器服务,从而确保访问质量;它对服务器和链路进行综合判断来决定由哪个地点的服务器来提供服务,实现异地服务器群服务质量的保证。红包使用独立的sh.vip.hongbao.qq.com域名。
  • TGW:Tencent Gateway,是一套实现多网统一接入,支持自动负载均衡的系统,TGW把外网不同运营商的请求,通过内网隧道转发给server,server返回数据时,再把数据通过内网隧道返回给TGW,再由TGW发送给不同的运营商。红包TGW外网部署了上海电信联通双VIP+中国香港CAP VIP。
  • QZHTTP:Web服务器,负责将HTTP请求转成逻辑层使用的WUP协议,采用同地多机房部署部署。QZHTTP作为TGW的RS,TGW会周期性的探测RS的状态,在1分钟内自动把故障RS从可服务列表中踢除,当TGW检测到RS恢复正常后,自动把它加回可服务列表中。由TGW提供负载均衡和容灾。

逻辑层

逻辑层使用SPP容器开发,礼包列表/区服选择/礼包发货三个功能均是无状态服务,多个服务器在服务容量足够的情况下任意踢掉一部分都不会影响正常服务,使用L5进行负载均衡/容灾/过载保护。

L5:机器级别容灾,业务程序调用L5 API从L5 Agent获取后台服务器的(IP, Port),使用该(IP, Port)对应的后台服务器进行通信,访问结束时调用L5 API上报访问接口和处理时延,L5 Agent对L5 API 上报的访问结果和处理时延进行统计和上报,当服务器出现故障,L5一般在1到2分钟内就会自动剔除故障服务器。

数据层

数据层主要使用了作为K-V存储的CMEM和作为分布式消息队列的RocketMQ,这两种服务都采用接入层-存储层划分,逻辑层访问数据层的接入层使用L5进行负载均衡/容灾,数据层的存储层都采用一主一备双机热备容灾,并落地磁盘支持掉电重启后重建内存数据,支持多机容灾但是不支持多机房多地容灾。

过载保护

红包入口流量说是8W/s,万一基础侧有问题发到了20W/s怎么办?

每个模块的容量都是从入口流量按照用户行为漏斗模型推算转化率设计的,万一评估有误转化率偏高超过了设计容量怎么办?

对于可能出现的超过了设计容量的流量尖峰,就要应用过载保护方法,保障系统能拒绝超过容量的部分请求,保障设计容量内的请求还能正常响应,实施的时候有四个要注意的地方:

  • 从源头上减少无效请求;
  • 从接入层开始拒绝;
  • 各层互相不信任;
  • 要照顾用户的情绪。

流量预加载

CDN做为页面访问的关键路径,前端页面制作离线包,预先下发到客户端,减少除夕当天CDN的流量压力。

频率限制

前台对用户发起请求的频率进行限制,超出频率的请求提示用户失败而不走到后台(每5秒只允许请求一次到后台),前台保护后台。

后台接入层根据压测数据配置CGI接口的每分钟接受的请求数,超出接口处理能力的请求丢弃并进行告警。接入门神系统,配置IP/uin/refer等规则限制恶意用户刷请求,保障服务的正常运行。

降级开关

前台调用后台的接口,设置开关以一定概率丢弃请求,对于关键路径返回错误提示用户稍后重试,对于非关键路径提供降级体验,结合频率限制功能,可以限制前台的流量传递到后台的比例,当比例设为0的时候则关闭该模块,实现前台保护后台。

队列堆积丢弃

后台逻辑层使用SPP框架,worker处理消息前先检查消息在SPP消息队列中等待时间是否超出了预设阈值(500ms),在队列中堆积过久的消息前端已经超时,对于用户已经无意义,服务丢弃请求并进行告警,预防队列式过载雪崩。

柔性可用

柔性可用,柔性是为了保护系统,保证系统整体的稳定性,可用性。可用是为了用户,尽最大努力为用户提供优质的体验(可能是部分服务体验)。一个是从系统本身角度出发,一个是从用户角度看,在真正实施过程中只有将两者分析清,并融合在一起才能真正做到系统的柔性可用。关键问题是找准用户的核心诉求,找出符合求场景的核心诉求作为关键路径,出现异常可以放弃的诉求作为非关键路径。

找准用户的核心诉求

春节游戏红包用户的核心诉求有三个:

  • 看到礼包列表
  • 选择区服角色
  • 领取礼包到账

其他的都可以作为非关键路径,有可以提高用户体验,没有也不影响用户的核心诉求。

保障关键路径的可用

看到礼包列表:作为页面关键模块的礼包列表,在红包活动前,十种游戏的礼包内容作为前端静态数据已经预先通过离线包/CDN下发。红包活动时,后台接口根据用户偏好返回的游戏礼包列表,只是提供前端礼包内容进行过滤和排序,失败了也有前端默认的游戏礼包列表,用户依旧能看到礼包列表,只是排序不够智能化。

选择区服角色:除夕前一周游戏中心的主站页面和运营活动增加一个后台接口请求,预先请求用户的区服角色信息缓存到本地,既降低了除夕当天的区服接口请求量又保证了游戏中心核心用户的区服信息是有效的。

领取礼包到账:RocketMQ对于领取操作是关键路径服务,用户领取礼包后需要写入RocketMQ才能算成功。故业务对消息队列做了逻辑层面的容灾,当RocketMQ出现故障时,可以打开容灾开关,领取操作写完应发流水后直接返回成功,不再往RocketMQ写入消息,采用分时段对账的方法替代实时发货,达到消息队列容灾效果,参见容错需求开发。

放弃异常的非关键路径

  • 前端页面展示模块化,对于请求数据不成功的非关键模块进行隐藏。
  • 红包页面导流到游戏中心,游戏中心展示按红点逻辑展示,只显示第一屏的数据,默认不加载第二屏数据,用户往下滚动时再加载,牺牲用户往下滚动会短暂卡顿的体验减少后台的请求压力。
  • 后台读取算法接口返回的推荐排序失败时使用默认的礼包排序。
  • 后台读取CMEM接口返回的礼包是拉活跃还是拉新失败的时每款游戏默认返回低价值的拉活跃礼包。

立体监控

“我们对外提供的服务是否正常的么?怎么证明我们的服务是没有问题的?”,是监控告警首先要回答的根本问题。有效的监控告警需要保证能完备地监测业务指标,当发现问题时能有效通知负责人并帮助分析问题,强调的是“完备性”和“有效通知”,两者缺一不可。春节红包的监控告警从用户、业务和机器三个层面上描述。

用户层面

从用户的角度监控系统是否有问题,模拟用户行为从系统外部发起请求,判断请求结果和时延是否符合预期,使用的是ATT的自动化用例。

ATT,autotest,是社交、开放平台测试组使用的测试管理工具,它是功能用例、自动化用例管理的平台。通过模拟真实的用户访问并校验返回数据,确认访问延时、功能正确性的用户层的监控手段,从业务侧进行实施监控功能的正常运行状态的工具。

业务层面

监控红包系统内部的各个子业务模块是否运行正常,分为两种:

1. 模块间的调用监控

监控系统内部各个模块间的调用是否正常,使用的是模调系统。

2. 模块内的状态监控

监控某个业务模块当前的状态是否正常,使用的是各个子系统自建的监控告警系统,春节红包这方面的监控主要有两个:AMS礼包领取剩余数量和消息队列消息堆积数量。春节红包由于准备的礼包数量较为充足,故没有引起告警;队列由于生成速度远超消费速度,设置的告警阈值是100W,但实际最终堆积超过了1200W,引发了告警。

机器层面

演习验证

压测演习

核心问题:系统能否抗住压力

细分又可以划为两个问题:

  • 对于系统容量内的合理请求,系统能否正常处理
  • 对于系统容量外的超额请求,系统能否成功丢弃

解决方案:全链路压测和单模块压测

对于未上线的接口的CGI Server和SPP Server,采用ApachBenchmark模拟请求压测。 对于已经上线的接口,除了隔离现网机器用ApachBenchmark模拟请求压测外,还使用了小组自研的压测系统,通过调整L5权重把现网流量逐步导到一台机器上来进行压测。

异常演习

核心问题:系统发生异常时各种柔性逻辑/容灾措施能否生效

解决方案:验证柔性逻辑和容灾措施

在红包正式上线前,通过模拟故障发生的真实异常场景,列出重点可能发生的故障问题,验证柔性逻辑和容灾错误是否真实有效。

经测试同学验证,checklist中的柔性逻辑和容灾措施确切有效,符合预期。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-08-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JAVA高级架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档