编者注
相比传统的红包,近两年风靡的“红包”如今似乎成了春节的一大亮点。经过千百年的传承变迁,春节发红包早已成为历史沉淀、融入民族血脉的文化习俗。据各公司公布的数据,除夕当日微信用户红包发送总量达80.8亿个,红包收发峰值达每秒40.9万个。春晚直播期间,讨论春晚的微博帖子达5191万条,网友互动量达1.15亿次,网友抢微博红包总次数超8亿次。
为此,我们策划了“春节红包”系列文章,为读者解析各平台红包活动背后的技术细节,本文讲的是微信。
距离2016年春节三波红包——摇红包事件已经过去一段时间了。面对动辄数十亿的企业资金,数十亿的红包,过程并不简单,充满变数,稍有不慎就可能导致失败。我们先在这里回顾一下筹备过程,看看技术如何为这个只许成功不许失败的项目保驾护航。
1.跨年活动红包系统
红包体系由信息流、业务流、资金流三部分组成,这三部分在组织架构上分别由不同的后端团队完成:信息流-微信后端、业务流-微信支付后端、资金流-财付通后端。
通常情况下,红包系统主要处理个人对话中以消息形式发送的红包。信息流主要包括用户操作背后的请求沟通以及不同用户、群组之间的红包消息流转;业务流是用户请求触发的红包打包、抢夺、开封等业务逻辑;资金流是红包背后的资金划转、入账过程。
跨年活动期间红包系统的实现和平常有些不同,跨年活动期间,红包系统除了处理个人红包外,还要处理大量的企业红包,这些红包由后端通过摇一摇功能进行集中分发,这里的信息流实现变化比较大。
接下来我给大家简单介绍一下2016年跨年活动时的红包系统架构,包括资源预下载、摇一摇红包、开红包三个方面。
(点击图片放大)
1 资源预下载
在除夕夜,用户可以通过摇一摇手机参与活动,领取红包或者其他活动页面。这些页面需要大量的图片、视频或者H5页面等资源。活动期间,参与用户众多,资源请求量大,如果全部实时在线访问,服务器的网络带宽将面临巨大压力,基本无法支撑。另外资源体积比较大,下载到手机上需要很长时间,用户体验也会很差。所以我们采用预下载的方式,在活动开始前几天将资源推送到客户端,客户端需要使用的时候直接从本地加载。
2 摇一摇/打开红包
跨年摇一摇子系统是针对此次活动专门定制的,各项活动按照时间线进行,其中最重要也是呼声最高的就是摇红包了。从需求来看,系统需要完成两件事:用户摇一摇就能抢到红包,并且红包金额能存入用户的支付账户。跨年当天,系统需要在极短的时间内发放数十亿个红包,对性能和可用性的要求很高。考虑到涉及资金的业务逻辑相对复杂,数据库事务多,耗时会比较长,所以我们将抢红包(信息流)和红包记账逻辑(业务流和资金流)异步化。前一部分处理流程设计得尽量轻量,让用户快速抢到红包,之后剩下的记账逻辑再异步完成。
那么,如何让抢红包环节既轻量又可靠呢?
1)零 RPC 调用
在微信后台系统中,一般情况下客户端发起的请求都会通过接入服务转发到具体的业务服务,会产生 RPC 调用。但对于摇一摇请求,我们将摇一摇逻辑直接嵌入到接入服务中,接入服务可以直接处理摇一摇请求并发放红包。
2)零数据库存储
按照一般的系统实现,用户看到的红包都是系统数据库中的数据记录,抢红包就是找到可用的红包记录,并将该记录标记为属于某个用户。在这种实现中,数据库是系统的瓶颈,也是主要的成本开销。我们在这个过程中完全不使用数据库,可以实现几个数量级的性能提升,同时还能保证更好的可靠性。
3)异步
用户抢到红包后,后续的账户处理并不会同步进行,请求会放到红包异步队列中,再通过异步队列转给微信支付后端,由微信支付后端完成后续的业务逻辑。
确保大规模集群中的数据一致性
其实网络分裂很难从根本上避免,我们在设计系统的时候就假设网络分裂一定会发生,基于此思考应该采取什么解决方案才能保证网络分裂时系统能够正常运行。
我们的解决方案是在每个数据中心建设三个独立的数据园区,如果任何一个数据园区遇到网络分裂,甚至成为孤岛,另外两个数据园区依然可以无损处理整个数据中心的请求。
三园区容灾的关键是数据一致性,我们需要保证拆分数据园区的数据在其他园区有强一致副本,这样请求落在另外两个园区之后,服务才能无损完成。另外故障园区恢复之后,所有园区的数据能够自动保持强一致性。微信后端实现了一套基于算法保证数据强一致性的存储系统(这个存储系统在另一篇文章中介绍)。另外还有可靠的异步队列可以为三个园区提供强一致性保证,这次的红包系统中就应用了该队列。前面提到的接入服务上部署的红包文件,其实是可以实现三园区容灾的,每个接入服务上部署的红包文件在其他数据园区都会有备份,当一个数据园区发生故障时,我们可以将故障园区的红包分发到其他数据园区。
今年活动的红包总量很大,活动形式也更加丰富,我们在以下几个方面做了优化。
1 服务绩效
为了提升各个业务模块的处理性能,我们通过压力测试和分析,发现了很多性能瓶颈,并做了大量优化。
2 业务支持能力
它支持更加复杂的业务场景,并在客户端和服务端都增加了很多预构建的能力,后期可以灵活调整,以更好地服务于产品运营。
3 可用性
持续提升系统可用性是我们的目标,以下五点可以大大提升系统可用性。
1)系统容量评估及配额
系统的容量需要准确评估和验证,结合业务设计合理的配额方案和降级方案,尽可能保证系统不会超负荷运行。例如,我们在评估验证了系统每秒开红包的最大数量后,在处理用户摇一摇请求时,可以限制每秒发红包的最大数量,这样就间接保证了开红包的数量不会超过处理能力。
2)过载保护
如果某个服务过载了,它要能够自我保护,不至于不堪重负,更不能波及到系统中的其他服务。我们在后端服务框架层面有通用的过载保护能力:如果服务无法处理,会根据请求的优先级,尽快丢弃超出处理能力的请求,保证服务的有效输出;当某些服务实例过载时,上游调用端可以自动做负载均衡调整,将请求调整到负载较低的服务实例上;如果上游调用端发现大部分服务实例过载,也可以主动丢弃部分请求,减轻后端服务器的负担。
3)减少关键路径
减少核心用户体验涉及的步骤和模块,集中精力保证关键路径的可用性,从而整体提升可用性。我们正是基于这样的考虑,将活动红包的信息流和业务流异步化。核心用户体验相关的抢红包操作,只需要信息流中的接入服务、红包简化逻辑服务、红包异步队列(入口)三个服务模块的参与即可完成。这三个服务模块可以用相对较低的成本做到高可用,而且可以更好地规避业务流和资金流中几十甚至上百个服务模块可能产生的风险。
4)监测指标
我们需要对系统的真实负载有一个准确及时的了解,所以必须要有一套高效可靠的监控系统,以及一套有效的监控指标。监控指标越多越好,太多会影响判断。一定要有几个能够准确反映问题的核心指标。在我们的系统中,这些核心指标一般都是集成在基础框架中的。根据经验,最有用的一个指标就是服务最终的系统故障。我们将服务故障分为两类:逻辑故障和系统故障。系统故障一般是由于服务暂时不可用导致的,可以通过重试自动解决。如果重试多次请求,系统仍然失败,则发生最终的系统故障。最终的系统故障通常可以快速定位异常服务并及时处理。
5)人为干预
我们在红包系统中预置了很多配置开关,当自动过载保护未能起到预期的作用时,我们可以进行人工干预,利用这些有保障的手动开关,快速降低负载,恢复服务。
三大技术创新
其实这类活动用到的技术都是现成的,并不复杂。但为什么人们觉得很难实现呢?主要因为规模:并发用户数和请求数越大,系统成本和可用性就越难取得平衡,也就是越难实现一个运行成本低、服务可用性高的系统。
传统的应对此类海量用户活动的实施方案,一般会在客户端对请求进行过滤,并以一定的概率(根据时间或者交互次数)发送到服务器进行处理,这样可以大大减轻服务器的压力。
我们认为可以做得更好,在这种活动的技术方案上有所突破——充分处理每一次用户交互,同时保持较低的成本。这大大降低了客户端的实施风险(因为客户端的更新和覆盖周期相对较长)。此外,服务器对用户交互有全面的控制和灵活的调整能力。
这些能力对于活动运营来说非常宝贵,让我们能够在活动过程中针对各种复杂的用户场景进行精细化调整,让产品运营拥有极大的灵活性,保证活动效果和用户体验。请看下面两个例子。
于是我们对这个技术方案进行了全面的思考和设计,最终实现了现在的系统,能够以极低的成本达到极高的性能和可用性,并成功运用在跨年活动中。
四种服务降级场景
我们对2016年1月26日摇一摇/朋友圈红包照活动前期活动和2016年2月7日正式活动进行了详细的回顾,回顾了活动期间业务数据是否符合预期,各个模块的表现是否符合预期,并对各类表现未达到预期的原因及解决方案进行了分析。
红包系统针对信息流、业务流、资金流的降级方案有很多,以保证核心的用户体验,下面列举几个信息流降级方案的例子。
a)如果数据园区出现网络分裂等故障,完全无法使用,部署在那里的红包如何派送?
虽然红包文件在园区之间是冗余存储的,但出于性能和可用性的考虑,我们并不打算在园区之间维护强一致的红包发放记录,实现记录级别的“断点续传”。而是会将红包文件按时间段进行切分,降级为文件级别的“断点续传”。当某个园区不可用时,使用降级方案后,故障园区当前发放周期的红包文件将不会发放,只能通过其他园区正常发放下一周期的红包。
b) 如果活动期间的用户交互量超出了服务的处理能力怎么办?
如上所述,我们很难准确估算参与的用户数量和互动次数。针对本次活动,我们在系统设计之初就估算了会有 2000 万次/秒的峰值请求,并在系统最终实施部署时预留了一定的余量,提供了估算值 2.5 倍(即 5000 万次/秒峰值请求)的系统处理能力。在除夕夜,服务器处理了 2500 万次/秒的峰值请求,实际服务负载不足 50%。但如果当时用户过多、互动过于火爆,到达后台的请求数超过 5000 万次/秒,系统就会进入降级模式,客户端可以在服务器的控制下减少请求,防止服务器过载。
c) 红包发放太快,后端处理不了怎么办?
如上文所述,用户在信息流中抢红包,完成后请求被放入红包异步队列中再转给业务流,如果红包请求数量过多,会导致队列积压,导致红包延迟发放,但不会导致用户请求失败。
类似的降级方案还有很多,每个环节又有几种不同的降级方案,有的专门针对业务设计(如a、b),有的利用基础组件/服务/解决方案固有的降级能力(如c)。这些方案都遵循一个原则:降级的时候尽量保证用户的核心体验。
5. 资金和红包准备困难
一般来说,跨年活动资金和红包的准备有四个难点。
1)红包数量
2)资金到位
3)资本预算分配方案(资本脚本)
4)资金安全
微信摇企业红包全流程
如果在除夕夜的摇号过程中,按照上面说的超级复杂的配置方案,瞬间生成随机红包,这显然是一个高风险、极其复杂的逻辑。一个只能成功不能失败的项目,主流程必须极其简单高效,所以这里所有的资金和红包都需要按照方案规则提前分割准备好。
精准部署预先生成的红包数据(预红包数据),摇红包资金和红包准备整体流程方案有两种选择。
(点击图片放大)
方案一:提供前置红包数据并部署到微信接入机,写入红包DB,红包接入机在摇红包过程中控制发放红包,开红包时修改红包DB中的红包数据; - 方案二:只提供前置红包数据并部署到微信接入机,红包接入机在摇红包过程中控制发放红包,开红包时直接写入红包DB。
第二种方案减少了一次DB操作,如果红包数据量是百亿级的话,可以大大减少数据导入、对账等活动的准备时间,特别是需要变更方案的时候。
七、充分准备 1、合理建模资本预算和资本情景
首先,面对如此庞大的资金量、复杂的资金场景,如何才能精准高效地管控逻辑?要消除笨拙与粗暴,我们需要一个优雅的解决方案——建模。
(点击图片放大)
对资本预算和资本脚本进行合理的建模,让模型覆盖、管理和控制一切——让工具基于模型进行自动处理和验证。这里需要做的是:
其次,如何支撑前面提到的众多变量、调整和变化?在建立回归模型时,我们仍然需要考虑对同一预算的多种资金分配方案的支持。
(点击图片放大)
针对同一份资金预算,可同时或依次生成多套预发红包数据文件,以便提前做好多项准备,方便计划变更。
再者,这么大的资金意味着这么大的诱惑,会不会出现问题?
墨菲定律要求我们关注上述安全风险,解决的办法就是加密——对预发红包数据进行加密,加密库和解密库独立维护,保证密钥不泄露,在工具生成预发红包数据时,用密钥进行加密,在部署、存储、传输过程中,预发红包数据始终保持加密状态,只有在拆分红包的逻辑中,通过编译二进制压缩库才能解密。
同时鸡蛋不能全部放在一个篮子里,为了控制密钥泄露带来的影响,确保财务风险可控,整个预生成的红包数据可能要用到几百到几千个密钥加密,一个密钥加密的红包资金金额在20-30万。解密库还需要能够设置密钥ID的白名单和黑名单,确保未使用的密钥无法使用,确认泄露的密钥能够被拦截。
2 极度压缩
如果有几百亿个红包,如果不进行压缩的话,预发红包的数据文件大小会非常恐怖,几百GB、几百TB的数据在传输和部署上是非常困难的,一旦出现调整或者变化,就会变得非常困难,所以需要将数据压缩到极致。
3. 和解
以上都做了是不是就安全了?如果真的有人写了一个不存在的红包怎么办?还有其他的潜在风险没有考虑到?所以我们需要一个后盾——对账,得把所有事情都搞清楚了才能安心。
对账后记录时长限制为 30 至 60 分钟,会造成不好的用户体验。为了提升用户体验,将大量预红包数据(占比约 10%)导入 KV(缓存),开红包时实时验证,转账立即记录。未命中 KV 的红包会等待对账异步完成记录。
概括
我们未来可能还会继续有类似的活动,虽然我们已经有一套经过验证的、可复用的技术方案,但我们希望继续优化和实现一些可复用的模块和服务,可以快速应用到下一次活动或者其他业务场景中。比如我们在 2015 年完成第一个跨年活动之后,就完善和泛化了资源预下载系统,然后应用到多个业务场景和这次 2016 年跨年活动中。未来会有更多类似的系统和服务可以复用和泛化。
关于作者
张文瑞一直从事后端系统的设计开发工作,早期涉足传统行业软件,后投身互联网,作为微信最早的后端开发人员之一,亲眼见证了微信从无到有、逐渐壮大的过程。
王鹏程是微信支付商户系统开发团队负责人,专家级工程师,2008年加入腾讯,拥有6年电商经验,涉及C2C、B2C、ERP等,2年第三方支付开发经验。
感谢陈星路( Chen)审阅本文。
如需为中文站做贡献或参与内容翻译,请发邮件至 。也欢迎您通过新浪微博(@、@丁晓昀)、微信(微信号: )关注我们。