流水不争先,争的是滔滔不绝

快速裂变:见证微信强大后台架构从0到1的演进历程(二)

微信QQ 云聊IM 948℃

接上文:快速裂变:见证微信强大后台架构从0到1的演进历程(一)

阶段四:平台化

2011.8 深圳举行大运会。微信推出“微信深圳大运志愿者服务中心”服务号,微信用户可以搜索“szdy”将这个服务号加为好友,获取大会相关的资讯。当时后台对“szdy”做了特殊处理,用户搜索时,会随机返回“szdy01”,“szdy02”…,“szdy10”这10个微信号中的1个,每个微信号背后都有一个志愿者在服务。

2011.9 “微成都”落户微信平台,微信用户可以搜索“wechengdu”加好友,成都市民还可以在“附近的人”看到这个号,我们在后台给这个帐号做了一些特殊逻辑,可以支持后台自动回复用户发的消息。

这种需求越来越多,我们就开始做一个媒体平台,这个平台后来从微信后台分出,演变成了微信公众平台,独立发展壮大,开始了微信的平台化之路。除微信公众平台外,微信后台的外围还陆续出现了微信支付平台、硬件平台等等一系列平台。

阶段五:走出国门

微信走出国门的尝试开始于3.0版本。从这个版本开始,微信逐步支持繁体、英文等多种语言文字。不过,真正标志性的事情是第一个海外数据中心的投入使用。

1、海外数据中心

海外数据中心的定位是一个自治的系统,也就是说具备完整的功能,能够不依赖于国内数据中心独立运作。

【(1)多数据中心架构】

多数据中心架构

系统自治对于无状态的接入层和逻辑层来说很简单,所有服务模块在海外数据中心部署一套就行了。

但是存储层就有很大麻烦了——我们需要确保国内数据中心和海外数据中心能独立运作,但不是两套隔离的系统各自部署,各玩各的,而是一套业务功能可以完全互通的系统。因此我们的任务是需要保证两个数据中心的数据一致性,另外Master-Master架构是个必选项,也即两个数据中心都需要可写。

【(2) Master-Master 存储架构】

Master-Master架构下数据的一致性是个很大的问题。两个数据中心之间是个高延时的网络,意味着在数据中心之间直接使用Paxos算法、或直接部署基于Quorum的KVSvr等看似一劳永逸的方案不适用。

最终我们选择了跟Yahoo!的PNUTS系统类似的解决方案,需要对用户集合进行切分,国内用户以国内上海数据中心为Master,所有数据写操作必须回到国内数据中心完成;海外用户以海外数据中心为Master,写操作只能在海外数据中心进行。从整体存储上看,这是一个Master-Master的架构,但细到一个具体用户的数据,则是Master-Slave模式,每条数据只能在用户归属的数据中心可写,再异步复制到其他数据中心。

多数据中心的数据Master-Master架构

【(3)数据中心间的数据一致性】

这个Master-Master架构可以在不同数据中心间实现数据最终一致性。如何保证业务逻辑在这种数据弱一致性保证下不会出现问题?这个问题可以被分解为2个子问题:

用户访问自己的数据:

用户可以满世界跑,那是否允许用户就近接入数据中心就对业务处理流程有很大影响。如果允许就近接入,同时还要保证数据一致性不影响业务,就意味着要么用户数据的Master需要可以动态的改变;要么需要对所有业务逻辑进行仔细梳理,严格区分本数据中心和跨数据中心用户的请求,将请求路由到正确的数据中心处理。

考虑到上述问题会带来很高昂的实现和维护的复杂度,我们限制了每个用户只能接入其归属数据中心进行操作。如果用户发生漫游,其漫游到的数据中心会自动引导用户重新连回归属数据中心。

这样用户访问自己数据的一致性问题就迎刃而解了,因为所有操作被限制在归属数据中心内,其数据是有强一致性保证的。此外,还有额外的好处:用户自己的数据(如:消息和联系人等)不需要在数据中心间同步,这就大大降低了对数据同步的带宽需求。

用户访问其他用户的数据:

由于不同数据中心之间业务需要互通,用户会使用到其他数据中心用户创建的数据。例如,参与其他数据中心用户创建的群聊,查看其他数据中心用户的朋友圈等。

仔细分析后可以发现,大部分场景下对数据一致性要求其实并不高。用户稍迟些才见到自己被加入某个其他数据中心用户建的群、稍迟些才见到某个好友的朋友圈动态更新其实并不会带来什么问题。在这些场景下,业务逻辑直接访问本数据中心的数据。

当然,还是有些场景对数据一致性要求很高。比方说给自己设置微信号,而微信号是需要在整个微信帐号体系里保证唯一的。我们提供了全局唯一的微信号申请服务来解决这一问题,所有数据中心通过这个服务申请微信号。这种需要特殊处置的场景极少,不会带来太大问题。

【(4)可靠的数据同步】

数据中心之间有大量的数据同步,数据是否能够达到最终一致,取决于数据同步是否可靠。为保证数据同步的可靠性,提升同步的可用性,我们又开发一个基于Quorum算法的队列组件,这个组件的每一组由3机存储服务组成。与一般队列的不同之处在于,这个组件对队列写入操作进行了大幅简化,3机存储服务不需要相互通讯,每个机器上的数据都是顺序写,执行写操作时在3机能写入成功2份即为写入成功;若失败,则换另外一组再试。因此这个队列可以达到极高的可用性和写入性能。每个数据中心将需要同步的数据写入本数据中心的同步队列后,由其他数据中心的数据重放服务将数据拉走并进行重放,达到数据同步的目的。

2、网络加速

海外数据中心建设周期长,投入大,微信只在香港和加拿大有两个海外数据中心。但世界那么大,即便是这两个数据中心,也还是没法辐射全球,让各个角落的用户都能享受到畅快的服务体验。

通过在海外实际对比测试发现,微信客户端在发消息等一些主要使用场景与主要竞品有不小的差距。为此,我们跟公司的架构平台部、网络平台部和国际业务部等兄弟部门一起合作,围绕海外数据中心,在世界各地精心选址建设了数十个POP点(包括信令加速点和图片CDN网络)。另外,通过对移动网络的深入分析和研究,我们还对微信的通讯协议做了大幅优化。微信最终在对比测试中赶上并超过了主要的竞品。
阶段六:精耕细作

1、三园区容灾

2013.7.22 微信发生了有史以来最大规模的故障,消息收发和朋友圈等服务出现长达5个小时的故障,故障期间消息量跌了一半。故障的起因是上海数据中心一个园区的主光纤被挖断,近2千台服务器不可用,引发整个上海数据中心(当时国内只有这一个数据中心)的服务瘫痪。

故障时,我们曾尝试把接入到故障园区的用户切走,但收效甚微。虽然数百个在线模块都做了容灾和冗余设计,单个服务模块看起来没有单点故障问题;但整体上看,无数个服务实例散布在数据中心各个机房的8千多台服务器内,各服务RPC调用复杂,呈网状结构,再加上缺乏系统级的规划和容灾验证,最终导致故障无法主动恢复。在此之前,我们知道单个服务出现单机故障不影响系统,但没人知道2千台服务器同时不可用时,整个系统会出现什么不可控的状况。

其实在这个故障发生之前3个月,我们已经在着手解决这个问题。当时上海数据中心内网交换机异常,导致微信出现一个出乎意料的故障,在13分钟的时间里,微信消息收发几乎完全不可用。在对故障进行分析时,我们发现一个消息系统里一个核心模块三个互备的服务实例都部署在同一机房。该机房的交换机故障导致这个服务整体不可用,进而消息跌零。这个服务模块是最早期(那个时候微信后台规模小,大部分后台服务都部署在一个数据园区里)的核心模块,服务基于3机冗余设计,年复一年可靠地运行着,以至于大家都完全忽视了这个问题。

为解决类似问题,三园区容灾应运而生,目标是将上海数据中心的服务均匀部署到3个物理上隔离的数据园区,在任意单一园区整体故障时,微信仍能提供无损服务。

【(1)同时服务】

传统的数据中心级灾备方案是“两地三中心”,即同城有两个互备的数据中心,异地再建设一个灾备中心,这三个数据中心平时很可能只有一个在提供在线服务,故障时再将业务流量切换到其他数据中心。这里的主要问题是灾备数据中心无实际业务流量,在主数据中心故障时未必能正常切换到灾备中心,并且在平时大量的备份资源不提供服务,也会造成大量的资源浪费。

三园区容灾的核心是三个数据园区同时提供服务,因此即便某个园区整体故障,那另外两个园区的业务流量也只会各增加50%。反过来说,只需让每个园区的服务器资源跑在容量上限的2/3,保留1/3的容量即可提供无损的容灾能力,而传统“两地三中心”则有多得多的服务器资源被闲置。此外,在平时三个园区同时对外服务,因此我们在故障时,需要解决的问题是“怎样把业务流量切到其他数据园区?”,而不是“能不能把业务流量切到其他数据园区?”,前者显然是更容易解决的一个问题。

【(2)数据强一致】

三园区容灾的关键是存储模块需要把数据均匀分布在3个数据园区,同一份数据要在不同园区有2个以上的一致的副本,这样才能保证任意单一园区出灾后,可以不中断地提供无损服务。由于后台大部分存储模块都使用KVSvr,这样解决方案也相对简单高效——将KVSvr的每1组机器都均匀部署在3个园区里。

【(3)故障时自动切换】

三园区容灾的另一个难点是对故障服务的自动屏蔽和自动切换。即要让业务逻辑服务模块能准确识别出某些下游服务实例已经无法访问,然后迅速自动切到其他服务实例,避免被拖死。我们希望每个业务逻辑服务可以在不借助外部辅助信息(如建设中心节点,由中心节点下发各个业务逻辑服务的健康状态)的情况下,能自行决策迅速屏蔽掉有问题的服务实例,自动把业务流量分散切到其他服务实例上。另外,我们还建设了一套手工操作的全局屏蔽系统,可以在大型网络故障时,由人工介入屏蔽掉某个园区所有的机器,迅速将业务流量分散到其他两个数据园区。

【(4)容灾效果检验】

三园区容灾是否能正常发挥作用还需要进行实际的检验,我们在上海数据中心和海外的香港数据中心完成三园区建设后,进行了数次实战演习,屏蔽单一园区上千台服务,检验容灾效果是否符合预期。特别地,为了避免随着时间的推移某个核心服务模块因为某次更新就不再支持三园区容灾了,我们还搭建了一套容灾拨测系统,每天对所有服务模块选取某个园区的服务主动屏蔽掉,自动检查服务整体失败量是否发生变化,实现对三园区容灾效果的持续检验。

2、性能优化

之前我们在业务迅速发展之时,优先支撑业务功能快速迭代,性能问题无暇兼顾,比较粗放的贯彻了“先扛住再优化”的海量之道。2014年开始大幅缩减运营成本,性能优化就被提上了日程。
我们基本上对大部分服务模块的设计和实现都进行了重新review,并进行了有针对性的优化,这还是可以节约出不少机器资源的。但更有效的优化措施是对基础设施的优化,具体的说是对Svrkit框架的优化。Svrkit框架被广泛应用到几乎所有服务模块,如果框架层面能把机器资源使用到极致,那肯定是事半功倍的。

结果还真的可以,我们在基础设施里加入了对协程的支持,重点是这个协程组件可以不破坏原来的业务逻辑代码结构,让我们原有代码中使用同步RPC调用的代码不做任何修改,就可以直接通过协程异步化。Svrkit框架直接集成了这个协程组件,然后美好的事情发生了,原来单实例最多提供上百并发请求处理能力的服务,在重编上线后,转眼间就能提供上千并发请求处理能力。Svrkit框架的底层实现在这一时期也做了全新的实现,服务的处理能力大幅提高。

3、防雪崩

我们一直以来都不太担心某个服务实例出现故障,导致这个实例完全无法提供服务的问题,这个在后台服务的容灾体系里可以被处理得很好。最担心的是雪崩:某个服务因为某些原因出现过载,导致请求处理时间被大大拉长。于是服务吞吐量下降,大量请求积压在服务的请求队列太长时间了,导致访问这个服务的上游服务出现超时。更倒霉的是上游服务还经常会重试,然后这个过载的服务仅有的一点处理能力都在做无用功(即处理完毕返回结果时,调用端都已超时放弃),终于这个过载的服务彻底雪崩了。最糟糕的情况是上游服务每个请求都耗时那么久,雪崩顺着RPC调用链一级级往上传播,最终单个服务模块的过载会引发大批服务模块的雪崩。

我们在一番勒紧裤腰带节省机器资源、消灭低负载机器后,所有机器的负载都上来了,服务过载变得经常发生了。解决这一问题的有力武器是Svrkit框架里的具有QoS保障的FastReject机制,可以快速拒绝掉超过服务自身处理能力的请求,即使在过载时,也能稳定地提供有效输出。

4、安全加固

近年,互联网安全事件时有发生,各种拖库层出不穷。为保护用户的隐私数据,我们建设了一套数据保护系统——全程票据系统。其核心方案是,用户登录后,后台会下发一个票据给客户端,客户端每次请求带上票据,请求在后台服务的整个处理链条中,所有对核心数据服务的访问,都会被校验票据是否合法,非法请求会被拒绝,从而保障用户隐私数据只能用户通过自己的客户端发起操作来访问。
阶段七:新的挑战

1、资源调度系统

微信后台有成千的服务模块,部署在全球数以万计的服务器上,一直依靠人工管理。此外,微信后台主要是提供实时在线服务,每天的服务器资源占用在业务高峰和低谷时相差很大,在业务低谷时计算资源被白白浪费;另一方面,很多离线的大数据计算却受制于计算资源不足,难以高效完成。

我们正在实验和部署的资源调度系统(Yard)可以把机器资源的分配和服务的部署自动化、把离线任务的调度自动化,实现了资源的优化配置,在业务对服务资源的需求有变化时,能更及时、更弹性地自动实现服务的重新配置与部署。

2、高可用存储

基于Quorum算法的KVSvr已经实现了强一致性、高可用且高性能的Key-Value/Key-Table存储。最近,微信后台又诞生了基于Paxos算法的另一套存储系统,首先落地的是PhxSQL,一个支持完整MySQL功能,又同时具备强一致性、高可用和高性能的SQL存储。

本文来自:WeMobileDev

版权声明:部分文章、图片等内容为用户发布或互联网整理而来,仅供学习参考。如有侵犯您的版权,请联系我们,将立刻删除。
点击这里给我发消息