文 / 腾讯 邓建俊算法
优测小优有话说:
你觉得优社区只有测试相关的知识吗?手机QQ大牛带你走一波红包后台设计!数据库
2016除夕夜注定是一个不平凡的夜晚,除了陪家人吃团圆饭、看春晚,还得刷一刷、摇一摇、咻一咻,忙得不亦乐。相信大部分读者也已经体验过手Q的刷一刷抢红包,玩法简单中奖率高,获得了许多用户的好评。
后端
那么对于后台而言,要实现这个亿万级用户的抢红包系统,咱们将会面临哪些问题?缓存
(1)海量的并发请求,预估峰值800w/s安全
800w/s的预估峰值请求,红包系统必需要保证在如此高并发的请求下,可以对每个请求都及时正确的处理并作出响应。架构
(2)亿万数量的红包如何派发并发
刷一刷红包的奖品,除了现金红包外,还有各类卡券(如京东券、滴滴打车券等)和个性装扮类奖品(如明星气泡、挂件等),如何保证亿万数量的奖品公平公正的发放,很少发、不错发,而且保证奖品尽量的发放出去,是红包系统面临的一道难题。另外,与各种奖品相对应的业务,他们的能力也不尽相同,奖品发放速度的控制也成为了后端业务系统正常运做的关键。运维
(3)安全如何保障分布式
对于一个掌握着亿万资金发放的系统,一些不法分子确定会千方百计利用各类漏洞来盗取资金,那么一套完备的安全防御措施就显得及其重要。高并发
(4)异常状况如何处理
刷一刷红包系统部署上线,真正提供抢红包服务也就几个小时的时间,常规系统的灰度发布、逐步优化的流程显然不适用。因此咱们必须分析出各个可能过载或故障的节点,并对这些节点设计好柔性策略。
在解决上面说起到的问题前,咱们先看看一个能基本运做的红包最小系统,以下图所示:
主要的处理流程以下:
首先,刷一刷是以活动的形式进行的,每轮活动的信息资源和商家素材资源会预先从CDN预下载到客户端,客户端在须要使用的时候,能够直接从本地加载,避免在抽奖峰值时下载从而对CDN带宽形成冲击,同时本地取资源也能够有更好的用户体验;
客户端判断到活动开始时,会展现出刷一刷的入口,用户进行刷的操做,会产生抽奖请求,抽奖请求发送到手Q接入层SSO,而后转发到抽奖逻辑层;
抽奖逻辑层通过一系列的逻辑判断,给客户端返回一个结果:中了现金、中了虚拟奖品或者没中奖,中奖后相关信息会保存在存储层;
若是是中了现金,交由财付通的支付系统对用户进行充值并发送公众号消息通知用户中奖;
若是是中了虚拟奖品,会调用基础IM后台的接口发送公众号消息通知用户中奖并提供领取入口。用户点击领取奖品,抽奖系统会给对应的业务返回领用资格和设置已领用标志。另外,对于某些内外部卡券类的奖品,抽奖系通通一只对接增值部的礼包发货系统,用户领用这部分奖品,会调用礼包发货系统接口,走MP营销平台进行发货。
那么,如何完善上述的系统,使之既可以知足业务的需求,又可以应对文章开头所描述的几个问题呢?
3.1 缓存机制
业务要求在每一个刷一刷的活动中,能对用户中奖次数、参与时间(30秒)进行限制。常规的实现里,用户的每一个抽奖请求到来时,先到存储层获取用户的中奖历史信息,断定用户是否还有资格进行抽奖,但在高并发的请求下,对存储层势必也会形成巨大的压力。因此这里咱们引入了本地内存缓存层,用于保存用户的中奖历史信息,每次请求到来时,会先到缓存层获取用户的中奖历史信息,若是在缓存层没找到,才会到存储层获取,这样就不会对存储层形成大压力,同时也能实现业务的需求。缓存层使用Memcached实现,但单靠这个缓存机制是否是就解决了存储层访问压力的问题了呢?
3.2 一致性hash寻址
虽然引入缓存层可以缓解存储层的访问压力,但别忘了,抽奖逻辑层是一个分布式的系统,在使用普通的路由算法下,同一个用户的请求不必定会落在同一台逻辑机器处理,也就是说,不必定可以命中以前保存在本地的缓存。因此为了使缓存生效,咱们在手Q接入层SSO使用了一致性hash的路由算法进行寻址,同一个uin的请求会落在同一台逻辑机器进行处理。
3.3 存储层选型
存储层的设计向来都是后台架构设计中的重点和难点。目前公司内部较成熟的存储系统有CMEM、Grocery,通过一番对比咱们选择使用Grocery,主要缘由有如下几点:
(1)强大的带条件判断的分布式原子算数运算
抽奖逻辑里须要对每一个奖品进行计数,避免多发少发,因此一个高效可靠的分布式原子加计数器显得格外重要,Grocery支持带条件判断的原子加计数器,调用一次接口就能完成奖品计数值与配额的判断以及奖品计数值的增长;
(2)灵活的数据类型
Grocery支持Key-Key-Row类型的数据存储格式,能够灵活的存储用户的红包中奖信息,为获取用户单个红包或者红包列表提供了丰富的接口;
(3)部署、扩容方便
Grocery在公司内部有专门的团队支持,易于部署和扩容。
3.4 奖品配额系统设计
刷一刷红包的玩法是以活动的形式存在的,业务要求每场活动要发放多少份的现金,多少份的虚拟奖品,每种奖品的发放比例是多少,这些都是该场活动的配额数据。另外,业务还要求奖品的发放额度要可以根据用户参与数灵活配置,保证在用户多的时候奖品多,并且无论在活动的任何一个有效时间点进来,都有机会中奖。
在活动期间,若是某个奖品对应的业务出现了故障,须要中止发放该奖品(发放比例修改成0),要求各台抽奖逻辑机器可以快速稳定的同步到修改的配置。基于以上几点咱们设计了这样一个配额系统:
在当天活动开始前,根据产品给的配额数据生成一份当天全部活动的Json格式配置;
配额管理工具能够对该Json配置进行相关合法性检查,检查OK后,把配置导入到Grocery数据库,并更新Seq;
运行在各台抽奖逻辑机器的配额agent会按期的检查Grocery里的Seq,若是Seq发生了变化,代表配额数据发生了变化,而后就会从Grocery获取最新的配置,并更新到抽奖逻辑的本地共享内存shm;
抽奖逻辑的每一个进程也会按期检查本地shm的内容,发现有变化,就会从新加载shm的配置。
这样配额系统就实现了能够秒级别控制奖品的发放额度,能够随时根据现场状况调整发放比例,并在短期内(10s)同步配置到全部的抽奖逻辑机器。
3.5 奖品发放限频设计
每一种奖品,对应的业务都有他们本身的能力,且各业务的能力也不尽相同(如黄钻8w/s,京东1w/s)。通常的,咱们在配置配额时(按照100%的兑换率),都不会配置到各业务的峰值,但配额系统有一个风险的不可控制的,就是进来的用户数是不肯定的,但为了保证奖品都能发放出去,奖品前1秒没发完的额度,会累加到下1秒发放,这样奖品发放峰值就有可能超过业务能力。
举个例子,黄钻的处理能力为8w/s,第1秒有5w的配额,第2秒有5w的配额,假如从第1秒开始就有足够多的用户参与抽奖,用户中奖后100%会去兑换黄钻,那么就有5w/s的请求到达黄钻业务,业务可以正常处理。假如因为一些意想不到的状况(例如xx宝在第1秒也在搞活动吸引走了部分用户),第1秒只有1w的用户,第2秒忽然涌进9w/s的用户,那么第2秒将有9w/s的请求到达业务导致业务过载。
基于以上缘由,奖品发放限频逻辑就成为了保障后端业务正常运做的关键。那么,如何设计这个限频逻辑呢?首先想到的方法就是到存储层Grocery获取当前奖品计数器的值,拿回本地与前1秒的值作判断,便可保证不超峰值,但这种作法会对存储层形成巨大的冲击,显然的不可行的。那么如何在保证性能的前提下,精确实现这个限频逻辑呢?
咱们是这么作的,活动前抽奖逻辑层的机器数是肯定的,咱们只要控制每台机器奖品的最高发放频率,就能总体控制该奖品的发放频率,最坏的状况多是某些逻辑层机器故障了,可能会形成该奖品在配额充足的状况下不能按峰值发放,但咱们能够经过修改配置从新设置每台机器该奖品最高的发放频率来解决这个问题。对于单机奖品发放频率计数咱们使用了Memcached的increment原子加操做,以时间和奖品ID做为Key,计数值做为Value存储在内存里,便可实现精确的计数。
上图左侧是咱们一开始的实现,当时时间是以秒做为计数周期的,也就是说,若是配额和用户数都充足的话,奖品会在这1秒的最开始所有发送出去,这样的话,问题又来了,通常来讲业务方给的能力例如8w/s,是指在这1秒内相对平均的来了8w的请求,业务方恰好可以正确处理这8w的请求。可是若是请求是在1秒的最开始所有涌到业务方,受限于业务方不一样的架构实现,有可能会触发业务方的频率限制或者是过载保护。所以,咱们将计数周期调小到百毫秒,这样奖品就会在1秒内相对平均的发放,从而解决了上述问题。
3.6 抽奖算法设计
抽奖算法的设计并不复杂,大致流程以下:
须要注意的是,每一个奖品都有发放的时间段,只有在该时间段内,才会把该奖品放入奖池里。另外,从奖池里按照比例挑选奖品,只要该奖品未成功派发,就继续从奖池再次挑选,直到奖池空为止,这样就能保证奖品尽量的派发出去。
3.7 流水系统设计
流水系统主要用于活动后对奖品发放和设置领用进行统计和对帐。同时,该系统还对设置领用失败的请求进行重作,确保奖品发放到用户帐户里。流水系统架构以下:
因为流水须要记录用户中奖的信息和设置领用的的状况,数据量巨大,因此抽奖逻辑层本地采用顺序写文件的方式进行记录。抽奖逻辑层会按期的把本地的流水文件同步到远程流水系统进行汇总和备份,同时,流水系统会对领用失败的流水进行重作,发送请求到抽奖逻辑层,抽奖逻辑层会调用支付系统的接口完成领用操做。
3.8 安全防御设计
安全防御对于抽奖系统来讲,也是必不可少的,咱们有如下几种措施来保障系统的安全:
(1)帐号鉴权
对于到达抽奖系统的请求,都要求带上登陆态,利用基础IM后台完善的PTLogin接口,对用户进行身份验证。
(2)实时安全审计
对于每个抽奖请求,咱们都会记录必要的信息(如客户端IP、版本、序列号等),加上原始请求一并转发到安所有门,由安所有门对全部的抽奖请求进行多维度的监控,对恶意请求进行打击。
(3)uin请求频率限制
对于每一个用户(uin)的每种请求(抽奖、设置领用等),咱们都作了请求频率限制,超过频率限制的请求会直接返回错误。频率限制必要的信息(时间、令牌)存储在Memcached里。
3.9 完善后的抽奖系统
4.1 接入层SSO过载
接入层SSO是手Q终端使用后台服务的大门,若是SSO出现问题,后果将不堪设想。抽奖请求在SSO预估的峰值为800w/s,虽然评估预留了必定的余量,但评估的数据仍是具备风险的,因此在接入层SSO咱们作了如下保护措施:
(1)错峰机制
抽奖活动开始后,用户会随机被错峰必定的时间,避免大量用户在同一时间发起抽奖。错峰的最大时间配置会随活动配置一块儿保存在CDN资源库中,在活动开始前预下载到客户端本地。奖品的配额数据也会根据错峰后的用户模型进行合理配置,保证公平性。
(2)实时可调的抽奖间隔
客户端发起抽奖请求是有时间间隔的,默认间隔也是保存在活动配置里。若是发现SSO即将过载,SSO能够在抽奖回包里带上新的抽奖时间间隔,从而达到减小抽奖请求的目的。
4.2 抽奖逻辑层过载
抽奖逻辑层设计的峰值能力为300w/s,假如即将超过该峰值,会使用以下措施进行保护:
(1)SSO调整请求透过率
SSO设计了一个抽奖请求透过率的配置,默认100%透过,正常状况下,客户端发起的抽奖请求到达SSO后,都会转发到抽奖逻辑层。假如抽奖逻辑层即将过载,SSO调整该透过率,就会按照比例随机对请求进行直接回包,回包内容指示用户未中奖。
4.3 存储层Grocery过载
Grocery若是即将过载,也能够经过调整SSO透过率来减小请求。
4.4 财付通现金充值接口过载
财付通现金充值接口能力为20w/s,配额系统配置该峰值也是配置成20w/s,若是发现20w/s财付通出现异常,能够减少峰值,而后经过配额系统快速同步修改后的峰值到全部的逻辑层机器。
4.5 虚拟奖品对应业务过载
每一个虚拟奖品对应的业务都有本身的峰值能力,配额系统也是按照业务给定的峰值能力进行配置,若是业务反馈异常,能够实时修改业务对应奖品的峰值或直接关闭该奖品的发放。
4.6 其余异常处理
其余模块的异常通常都是能够经过调整中奖率、关闭开关或关闭重试进行处理:
接入层和抽奖逻辑层IDC级容灾部署;
业务接入织云运维自动化平台,支持平滑扩容,进程实时监控并支持秒起;
存储层数据双份容灾。
腾讯优测(utest.qq.com)
腾讯优测是专业的移动云测试平台,为应用、游戏,H5混合应用的研发团队提供产品质量检测与问题解决服务。
不只在线上平台提供「全面兼容测试」、「云手机」「缺陷分析」等多种自动化测试工具,同时在线下为VIP客户配备专家团队,提供定制化综合测试解决方案。真机实验室配备上千款手机,覆盖亿级用户,7*24小时在线运行,为各种测试工具提供支持。
(据说关注公众号立刻就有腾讯内部移动研发及测试彩蛋哦~)
感兴趣能够立刻加官群勾搭客服妹妹哦~ 优测官方群:214483489