微信红包是一个很神奇的存在。
在2014年春节,微信上线红包功能,一个月内,微信支付的用户从3000万激增到1亿,为微信在移动支付领域挣得了一席之地。如今,在我这个普通用户眼中,两大阵营从强弱悬殊,渐趋平分秋色,甚至后来居上。
微信红包成功的原因这里不谈,也没有能力去谈。若从产品定位,组织架构,用户体验,消费者心理等等去展开,每一个点都值得专业人士花时间去研究。
红包的火热程度已经成功带动了一些灰色产业,比如赌bo,衍生出了一整套各类目的“包赢技术”,其实,这些都是没有的,是后期加的特技。
在今年春节当天,用户红包总发送量达80.8亿,峰值收发量为40.9万个/秒。在面对亿级资金与亿级数量,整个团队所要考虑的东西,需要多么全面与多么深入。
这篇文章不写红包脚本,也不写红包玄学,只想写一写,你的红包翻越千山万水来看你,是多么不容易。
简要了解红包系统的运作,无论是对于技术大牛或技术小白,都是大有裨益的。大牛可以考虑其中更多细节,学习其中设计哲学,思考优化方案;小白(比如我)可以涨涨见识,拓展一下思维深度,不要以后在某些场合张口就是赶超BAT(不是我);至于非技术读者,完全也可以粗略一读,至少能知道自己的红包去哪了,在茶余饭后作为谈资云淡风轻间装一波x,也是很有意思的一件事。
组织架构
红包系统主要分为三部分:
- 信息流:主要包括用户操作背后的请求通信和红包消息在不同用户和群中的流转,由微信后台完成。
- 业务流:主要包括用户请求引发的包红包、抢红包和拆红包等的业务逻辑,由微信支付后台完成。
- 资金流:主要包括红包背后的资金转账和入账等流程由财付通后台完成。
春节特色
在春节期间,红包系统将发生变化,因为多了一项功能“摇一摇红包”。从而出现三个新的流程:资源预下载、摇红包、拆红包。
1.资源预下载
在参与摇红包活动时,需要用到很多的图片,视频,H5资源。由于用户量大,时间点集中,这对资源的请求量是非常庞大的,服务器将会面临巨大压力,同时用户体验也会受影响,所以,一般在春节前几天,会将活动资源推送到客户端,在用户需要时直接从本地加载。
2.摇红包
从需求上看,系统需要完成两件事:用户可以通过摇一摇抢到红包,红包金额可以入到用户的支付账户。
在除夕,系统需要在很短时间内将几十亿个红包发放下去,对性能和可用性要求很高。考虑到涉及资金的业务逻辑比较复杂,还有很多数据库事务处理,耗时会比较长。
于是将摇红包(信息流)和红包的账务逻辑(业务流和资金流)异步化。将前一部分处理流程尽可能设计得轻量,让用户可以很快抢到红包,然后再异步完成剩下的账务逻辑。抢红包阶段是怎样做到既轻量又可靠呢?
-
零RPC调用 在微信后台系统中,一般情况下客户端发起的请求都是通过接入服务转发给具体的业务服务处理的,会产生RPC调用。但对于摇一摇请求,我们将摇一摇逻辑直接嵌入接入服务中,接入服务可以直接处理摇一摇请求,派发红包。
-
零数据库存储 按一般的系统实现,用户看到的红包在系统中是数据库中的数据记录,抢红包就是找出可用的红包记录,将该记录标识为属于某个用户。在这种实现里,数据库是系统的瓶颈和主要成本开销。我们在这一过程完全不使用数据库,可以达到几个数量级的性能提升,同时可靠性有了更好的保障。
- 支付系统将所有需要下发的红包生成红包票据文件;
- 将红包票据文件拆分后放到每一个接入服务实例中;
- 接收到客户端发起摇一摇请求后,接入服务里的摇一摇逻辑拿出一个红包票据,在本地生成一个跟用户绑定的加密票据,下发给客户端;
- 客户端拿加密票据到后台拆红包,后台的红包简化服务通过本地计算即可验证红包,完成抢红包过程。
3.抢红包
- 异步化 用户抢到红包后不会同步进行后续的账务处理,请求会被放入红包异步队列,再通过异步队列转给微信支付后台,由微信支付后台完成后续业务逻辑。
大规模集群的数据一致性
网络分裂很难从根本上避免设计系统时都是假设网络分裂一定会出现,基于此思考应该采用什么方案保障系统在网络分裂时能正常运作。
使用的方案是在每个数据中心都建设三个独立的数据园区,可以做到在任意一个数据园区出现网络分裂等故障,甚至彻底变成园区孤岛后,另外两个数据园区可以无损承接整个数据中心的请求。
三园区容灾的关键就是数据一致性。需要做到在分裂出去的那个数据园区的数据在其他园区有个强一致的副本,这样请求落到其他两个园区后,可以无损完成服务,此外在故障园区恢复后,数据在所有园区还能自动保持强一致。微信后台实现了基于Quorum算法,对数据有强一致性保证的存储系统——KvSvr。此外还有可以提供三园区强一致保证的可靠异步队列,这次就应用在这个红包系统中。前边提到的部署在接入服务的红包文件实际上也是可以实现三园区容灾的,我们在每台接入服务部署的红包文件都会在其他数据园区有个备份。在某个数据园区故障时,我们可以在其他数据园区发放故障园区的红包。
可用性
- 系统容量评估与配额
对系统的容量需要有个准确的评估与验证,并结合业务设计合理的配额方案和降级方案,尽可能保障系统不会过载。例如,评估并验证完系统每秒最大拆红包量后,就可以在处理用户摇一摇请求时,限制系统每秒最大发放红包配额,这就间接保证了拆红包量不会超出处理能力。
- 过载保护
服务如果出现过载了,必须有能力自保,不被压垮,并且不扩散到系统其他的服务。后台的服务框架层面具备通用的过载保护能力:服务如果处理不过来,就按请求的优先级尽快丢掉超出处理能力的请求,保证服务的有效输出;上游调用端在部分服务实例过载时,能自动做负载均衡调整,将请求调整到负载较低的服务实例中;上游调用端发现大部分服务实例都出现过载,也可以主动丢掉部分请求,减轻后端服务器的负担。
- 减少关键路径
减少核心用户体验所涉及的步骤和模块,集中力量保证关键路径的可用性,从而在整体上提高可用性。把活动红包的信息流和业务流进行异步化,就是基于这个考虑。跟用户核心体验相关的抢红包操作,在信息流中的接入服务、红包简化逻辑服务和红包异步队列(入队)这三个服务模块参与下即可完成。这三个服务模块是可以比较容易用较低成本就做到高可用的,可以较好地规避业务流和资金流中几十甚至上百个服务模块可能出现的风险。
- 监控指标
要对系统的真实负载情况有准确及时的了解,就必须要有一套高效、可靠的监控系统,同时还要有一套有效的监控指标,监控指标不是越多越好,太多了反而会影响判断,必须要有能准确反映问题的几个核心指标。在系统里,这些核心指标一般在基础框架集成,根据经验来看,其中一个很有用的指标是服务的最终系统失败。服务的失败分为两类:逻辑失败和系统失败。系统失败一般是服务暂时不可用导致,是可通过重试来自动解决的,如果请求重试若干次仍然为系统失败,就产生最终系统失败。通过最终系统失败通常可以快速定位到异常的服务,及时进行处置。
- 人工介入
红包系统内预置了很多配置开关,当自动运作的过载保护无法发挥预期作用时,可以通过人工介入,使用这些保底的手动开关迅速降低负载、恢复服务。
服务降级方案
- 如果某一个数据园区出现网络分裂等故障,完全不可用了,部署在那里的红包怎么发下去?
红包文件虽然在园区间有冗余存储,但基于性能和可用性考虑,我们并不打算在各园区间维护强一致的红包发放记录,做到记录级的“断点续发”,而是将红包文件按时段进行切分,降级为只做文件级的“断点续发”。在某个园区不可用时,使用降级方案后,故障园区当前发放时段的红包文件不会接着发放,仅保证下一时段的红包能通过其他园区正常发出去。
- 活动过程中,如果用户的交互量超过服务的处理能力了怎么办?
正如前面所述,我们很难准确估计参与用户量及互动次数。就本次活动而言,在系统设计之初,我们评估有2000万次/秒的峰值请求,并在系统最终实现和部署时预留了一定的余量,提供了评估值2.5倍(也就是5000万次/秒峰值请求)的系统处理能力,除夕当晚服务器处理请求峰值达到2500万次/秒,服务实际负载低于50%。但如果当时用户过多,互动过于火爆,到达后台的请求超过5000万次/秒,系统就会进入降级模式,客户端可以在服务器的控制下,减少请求,防止服务器过载。
- 红包发放速度过快,后端处理不过来怎么办?
正如前面所述,用户抢红包是在信息流完成的,完成后请求放到红包异步队列再转到业务流。如果领取红包请求量过大,队列会出现积压,红包的入账会出现延时,但不会导致用户请求失败。
类似的降级方案还有很多,每个环节都有若干个不同的降级方案,有些是针对业务专门设计的(如a和b),还有些是使用基础组件/服务/方案本身就具备的降级能力(如c)。这些方案都遵循着一个原则:降级时,尽可能保证用户的核心体验。
准备
- 对资金预算和资金剧本进行合理的建模.
首先,面对如此大量的资金和复杂的资金剧本,如何准确高效地管理和控制逻辑呢?要杜绝傻大黑粗,我们需要一个优雅的解决方案——建模。
具体略,暂时还完全看不懂。
- 极限压缩
如果是百亿个红包,那么产生预红包数据文件的大小不经过压缩是非常恐怖的,传输部署几百GB或几TB的数据是很艰苦的,一旦发生调整变更会变得非常艰难,所以需要对数据进行极限的压缩。
- 对于支付单号、商户号、红包账户等信息,由工具导成配置文件,配置到拆红包逻辑中,加密的红包数据中仅用一个批次ID表达;
- 拆分红包ID,部分分段同样转为ID,解密库解密后利用配置进行还原;
- 加密部分(Ticket):红包ID、金额、批次ID、密钥ID,压缩到16字节;
- 单条红包记录二进制表达,压缩到26字节。
- 对账
上面所有都做到就安全了么?真的就有人写了一个不存在红包进来会怎样?是否还有其他未考虑到的潜在风险?所以我们需要一个兜底——对账,把一切都要对清楚才放心。
对账后再入账的时效在30~60分钟,会造成不好的用户体验。为了提升用户体验,将大额的预红包数据(占比10%左右)导入KV(高速缓存),拆红包时进行即时校验,即时进行转账入账,未命中KV的红包则等待对账后异步完成入账。
资金配置与资金预算要总分对账;
- 红包数据文件与资金剧本进行总分对账;
- 红包数据进行全局去重验证;
- 红包数据进行解密验证和金额验证;
- 如果密钥泄漏红包金额等被篡改,兜底要进行红包DB中已拆红包数据与预红包数据的对账后,才能实际进行转账入账。
技术部分来自微信支付开发组组长王鹏程博客