而在这同步机制的背后,需要一个高可用、高可靠的序列号生成器来产生同步数据用的版本号。这个序列号生成器我们称之为seqsvr,目前已经发展为一个每天万亿级调用的重量级系统,其中每次申请序列号平时调用耗时1ms,99.9%的调用耗时小于3ms,服务部署于数百台4核CPU服务器上。 本文会重点介绍seqsvr的架构核心思想,以及seqsvr随着业务量快速上涨所做的架构演变。
微信服务器端为每一份需要与客户端同步的数据(例如消息)都会赋予一个唯一的、递增的序列号(后文称为sequence),作为这份数据的版本号。 在客户端与服务器端同步的时候,客户端会带上已经同步下去数据的最大版本号,后台会根据客户端最大版本号与服务器端的最大版本号,计算出需要同步的增量数据,返回给客户端。这样不仅保证了客户端与服务器端的数据同步的可靠性,同时也大幅减少了同步时的冗余数据。
这里不用乐观锁机制来生成版本号,而是使用了一个独立的seqsvr来处理序列号操作,一方面因为业务有大量的sequence查询需求——查询已经分配出去的最后一个sequence,而基于seqsvr的查询操作可以做到非常轻量级,避免对存储层的大量IO查询操作;另一方面微信用户的不同种类的数据存在不同的Key-Value系统中,使用统一的序列号有助于避免重复开发,同时业务逻辑可以很方便地判断一个用户的各类数据是否有更新。
从seqsvr申请的、用作数据版本号的sequence,具有两种基本的性质:
举个例子,小明当前申请的sequence为100,那么他下一次申请的sequence,可能为101,也可能是110,总之一定大于之前申请的100。而小红呢,她的sequence与小明的sequence是独立开的,假如她当前申请到的sequence为50,然后期间不管小明申请多少次sequence怎么折腾,都不会影响到她下一次申请到的值(很可能是51)。
这里用了每个用户独立的64位sequence的体系,而不是用一个全局的64位(或更高位)sequence,很大原因是全局唯一的sequence会有非常严重的申请互斥问题,不容易去实现一个高性能高可靠的架构。对微信业务来说,每个用户独立的64位sequence空间已经满足业务要求。
目前sequence用在终端与后台的数据同步外,同时也广泛用于微信后台逻辑层的基础数据一致性cache中,大幅减少逻辑层对存储层的访问。虽然一个用于终端——后台数据同步,一个用于后台cache的一致性保证,场景大不相同。
但我们仔细分析就会发现,两个场景都是利用sequence可靠递增的性质来实现数据的一致性保证,这就要求我们的seqsvr保证分配出去的sequence是稳定递增的,一旦出现回退必然导致各种数据错乱、消息消失;另外,这两个场景都非常普遍,我们在使用微信的时候会不知不觉地对应到这两个场景:小明给小红发消息、小红拉黑小明、小明发一条失恋状态的朋友圈,一次简单的分手背后可能申请了无数次sequence。
微信目前拥有数亿的活跃用户,每时每刻都会有海量sequence申请,这对seqsvr的设计也是个极大的挑战。那么,既要sequence可靠递增,又要能顶住海量的访问,要如何设计seqsvr的架构?我们先从seqsvr的架构原型说起。
不考虑seqsvr的具体架构的话,它应该是一个巨大的64位数组,而我们每一个微信用户,都在这个大数组里独占一格8bytes的空间,这个格子就放着用户已经分配出去的最后一个sequence:cur_seq。每个用户来申请sequence的时候,只需要将用户的cur_seq+=1,保存回数组,并返回给用户。
预分配中间层
任何一件看起来很简单的事,在海量的访问量下都会变得不简单。前文提到,seqsvr需要保证分配出去的sequence递增(数据可靠),还需要满足海量的访问量(每天接近万亿级别的访问)。满足数据可靠的话,我们很容易想到把数据持久化到硬盘,但是按照目前每秒千万级的访问量(~10^7 QPS),基本没有任何硬盘系统能扛住。
后台架构设计很多时候是一门关于权衡的哲学,针对不同的场景去考虑能不能降低某方面的要求,以换取其它方面的提升。仔细考虑我们的需求,我们只要求递增,并没有要求连续,也就是说出现一大段跳跃是允许的(例如分配出的sequence序列:1,2,3,10,100,101)。于是我们实现了一个简单优雅的策略:
请求带来的硬盘IO问题解决了,可以支持服务平稳运行,但该模型还是存在一个问题:重启时要读取大量的max_seq数据加载到内存中。
我们可以简单计算下,以目前uid(用户唯一ID)上限2^32个、一个max_seq 8bytes的空间,数据大小一共为32GB,从硬盘加载需要不少时间。另一方面,出于数据可靠性的考虑,必然需要一个可靠存储系统来保存max_seq数据,重启时通过网络从该可靠存储系统加载数据。如果max_seq数据过大的话,会导致重启时在数据传输花费大量时间,造成一段时间不可服务。
为了解决这个问题,我们引入号段Section的概念,uid相邻的一段用户属于一个号段,而同个号段内的用户共享一个max_seq,这样大幅减少了max_seq数据的大小,同时也降低了IO次数。
目前seqsvr一个Section包含10万个uid,max_seq数据只有300+KB,为我们实现从可靠存储系统读取max_seq数据重启打下基础。
工程实现
工程实现在上面两个策略上做了一些调整,主要是出于数据可靠性及灾难隔离考虑
接下来我们会介绍seqsvr的容灾架构。我们知道,后台系统绝大部分情况下并没有一种唯一的、完美的解决方案,同样的需求在不同的环境背景下甚至有可能演化出两种截然不同的架构。既然架构是多变的,那纯粹讲架构的意义并不是特别大,期间也会讲下seqsvr容灾设计时的一些思考和权衡,希望对大家有所帮助。
seqsvr的容灾模型在五年中进行过一次比较大的重构,提升了可用性、机器利用率等方面。其中不管是重构前还是重构后的架构,seqsvr一直遵循着两条架构设计原则:
这两点都是基于seqsvr可靠性考虑的,毕竟seqsvr是一个与整个微信服务端正常运行息息相关的模块。按照我们对这个世界的认识,系统的复杂度往往是跟可靠性成反比的,想得到一个可靠的系统一个关键点就是要把它做简单。相信大家身边都有一些这样的例子,设计方案里有很多高大上、复杂的东西,同时也总能看到他们在默默地填一些高大上的坑。当然简单的系统不意味着粗制滥造,我们要做的是理出最核心的点,然后在满足这些核心点的基础上,针对性地提出一个足够简单的解决方案。
那么,seqsvr最核心的点是什么呢?每个uid的sequence申请要递增不回退。这里我们发现,如果seqsvr满足这么一个约束:任意时刻任意uid有且仅有一台AllocSvr提供服务,就可以比较容易地实现sequence递增不回退的要求。
但也由于这个约束,多台AllocSvr同时服务同一个号段的多主机模型在这里就不适用了。我们只能采用单点服务的模式,当某台AllocSvr发生服务不可用时,将该机服务的uid段切换到其它机器来实现容灾。这里需要引入一个仲裁服务,探测AllocSvr的服务状态,决定每个uid段由哪台AllocSvr加载。出于可靠性的考虑,仲裁模块并不直接操作AllocSvr,而是将加载配置写到StoreSvr持久化,然后AllocSvr定期访问StoreSvr读取最新的加载配置,决定自己的加载状态
同时,为了避免失联AllocSvr提供错误的服务,返回脏数据,AllocSvr需要跟StoreSvr保持租约。这个租约机制由以下两个条件组成:
这两个条件保证了切换时,新AllocSvr肯定在旧AllocSvr下线后才开始提供服务。但这种租约机制也会造成切换的号段存在小段时间的不可服务,不过由于微信后台逻辑层存在重试机制及异步重试队列,小段时间的不可服务是用户无感知的,而且出现租约失效、切换是小概率事件,整体上是可以接受的。
到此讲了AllocSvr容灾切换的基本原理,接下来会介绍整个seqsvr架构容灾架构的演变
最初版本的seqsvr采用了主机+冷备机容灾模式:全量的uid空间均匀分成N个Section,连续的若干个Section组成了一个Set,每个Set都有一主一备两台AllocSvr。正常情况下只有主机提供服务;在主机出故障时,仲裁服务切换主备,原来的主机下线变成备机,原备机变成主机后加载uid号段提供服务。
可能看到前文的叙述,有些同学已经想到这种容灾架构。一主机一备机的模型设计简单,并且具有不错的可用性——毕竟主备两台机器同时不可用的概率极低,相信很多后台系统也采用了类似的容灾策略。
主备容灾存在一些明显的缺陷,比如备机闲置导致有一半的空闲机器;比如主备切换的时候,备机在瞬间要接受主机所有的请求,容易导致备机过载。既然一主一备容灾存在这样的问题,为什么一开始还要采用这种容灾模型?事实上,架构的选择往往跟当时的背景有关,seqsvr诞生于微信发展初期,也正是微信快速扩张的时候,选择一主一备容灾模型是出于以下的考虑:
前两点好懂,人力、机器都不如时间宝贵。而第三点比较有意思,下面展开讲下
微信后台绝大部分模块使用了一个自研的RPC框架,seqsvr也不例外。在这个RPC框架里,调用端读取本地机器的client配置文件,决定去哪台服务端调用。这种模型对于无状态的服务端,是很好用的,也很方便实现容灾。我们可以在client配置文件里面写“对于号段x,可以去SvrA、SvrB、SvrC三台机器的任意一台访问”,实现三主机容灾。
但在seqsvr里,AllocSvr是预分配中间层,并不是无状态的。而前面我们提到,AllocSvr加载哪些uid号段,是由保存在StoreSvr的加载配置决定的。那么这时候就尴尬了,业务想要申请某个uid的sequence,Client端其实并不清楚具体去哪台AllocSvr访问,client配置文件只会跟它说“AllocSvrA、AllocSvrB…这堆机器的某一台会有你想要的sequence”。换句话讲,原来负责提供服务的AllocSvrA故障,仲裁服务决定由AllocSvrC来替代AllocSvrA提供服务,Client要如何获知这个路由信息的变更?
这时候假如我们的AllocSvr采用了主备容灾模型的话,事情就变得简单多了。我们可以在client配置文件里写:对于某个uid号段,要么是AllocSvrA加载,要么是AllocSvrB加载。Client端发起请求时,尽管Client端并不清楚AllocSvrA和AllocSvrB哪一台真正加载了目标uid号段,但是Client端可以先尝试给其中任意一台AllocSvr发请求,就算这次请求了错误的AllocSvr,那么就知道另外一台是正确的AllocSvr,再发起一次请求即可。
也就是说,对于主备容灾模型,最多也只会浪费一次的试探请求来确定AllocSvr的服务状态,额外消耗少,编码也简单。可是,如果Svr端采用了其它复杂的容灾策略,那么基于静态配置的框架就很难去确定Svr端的服务状态:Svr发生状态变更,Client端无法确定应该向哪台Svr发起请求。这也是为什么一开始选择了主备容灾的原因之一。
在我们的实际运营中,容灾1.0架构存在两个重大的不足:
在主备容灾中,Client和AllocSvr需要使用完全一致的配置文件。变更这个配置文件的时候,由于无法实现在同一时间更新给所有的Client和AllocSvr,因此需要非常复杂的人工操作来保证变更的正确性(包括需要使用iptables来做请求转发,具体的详情这里不做展开)。
对于第二个问题,常见的方法是用一致性Hash算法替代主备,一个Set有多台机器,过载机器的请求被分摊到多台机器,容灾效果会更好。在seqsvr中使用类似一致性Hash的容灾策略也是可行的,只要Client端与仲裁服务都使用完全一样的一致性Hash算法,这样Client端可以启发式地去尝试,直到找到正确的AllocSvr。
例如对于某个uid,仲裁服务会优先把它分配到AllocSvrA,如果AllocSvrA挂掉则分配到AllocSvrB,再不行分配到AllocSvrC。那么Client在访问AllocSvr时,按照AllocSvrA ->AllocSvrB ->AllocSvrC的顺序去访问,也能实现容灾的目的。但这种方法仍然没有克服前面主备容灾面临的配置文件变更的问题,运营起来也很麻烦。
最后我们另辟蹊径,采用了一种不同的思路:既然Client端与AllocSvr存在路由状态不一致的问题,那么让AllocSvr把当前的路由状态传递给Client端,打破之前只能根据本地Client配置文件做路由决策的限制,从根本上解决这个问题。
所以在2.0架构中,我们把AllocSvr的路由状态嵌入到Client请求sequence的响应包中,在不带来额外的资源消耗的情况下,实现了Client端与AllocSvr之间的路由状态一致。具体实现方案如下:
seqsvr所有模块使用了统一的路由表,描述了uid号段到AllocSvr的全映射。这份路由表由仲裁服务根据AllocSvr的服务状态生成,写到StoreSvr中,由AllocSvr当作租约读出,最后在业务返回包里旁路给Client端。
把路由表嵌入到取sequence的请求响应包中,那么会引入一个类似“先有鸡还是先有蛋”的哲学命题:没有路由表,怎么知道去哪台AllocSvr取路由表?另外,取sequence是一个超高频的请求,如何避免嵌入路由表带来的带宽消耗?
这里通过在Client端内存缓存路由表以及路由版本号来解决,请求步骤如下:
基于以上的请求步骤,在本地路由表失效的时候,使用少量的重试便可以拉到正确的路由,正常提供服务。
到此把seqsvr的架构设计和演变基本讲完了,正是如此简单优雅的模型,为微信的其它模块提供了一种简单可靠的一致性解决方案,支撑着微信五年来的高速发展,相信在可预见的未来仍然会发挥着重要的作用。
本文系 微信后台团队 ,如有侵犯,请联系我们立即删除
我们的官网及论坛: OpenIM官网
OpenIM官方论坛
微信——腾讯战略级产品,创造移动互联网增速记录,10个月5000万手机用户,433天之内完成用户数从零到一亿的增长过程,千万级用户同时在线,摇一摇每天次数过亿...在技术架构上,微信是如何做到的?日前,在腾讯大讲堂在中山大学校园宣讲活动上,腾讯广研助理总经理、微信技术总监周颢在两小时的演讲中揭开了微信背后的秘密。
周颢把微信的成功归结于腾讯式的“三位一体”策略:即产品精准、项目敏捷、技术支撑。微信的成功是在三个方面的结合比较好,能够超出绝大多数同行或对手,使得微信走到比较前的位置。所谓产品精准,通俗的讲就是在恰当的时机做了恰当的事,推出了重量级功能,在合适的时间以最符合大家需求的方式推出去。他认为在整个微信的成功中,产品精准占了很大一部分权重。
相关链接
周颢
2001 年毕业于华南理工大学,计算机专业硕士。
2005 年加入腾讯广州研发部,历任 QQ 邮箱架构师,
广研技术总监,T4 技术专家,微信中心助理总经理。
微信研发团队里鼓励一种试错的信仰:他们坚信,在互联网开发里,如果能够有一个团队在更短的时间内尝试了更多机会(并能改进过来),就能有(更多的)机会胜出。敏捷是一种态度,在软件开发过程中,项目管理者都会非常忌讳“变更”这个词,但是在微信的项目运作中是不可以的。因为微信必须要容忍说哪怕在发布前的十分钟,也要允许他变更。这是非常大的挑战,因为打破了所有传统项目开发的常识。所有人都说不可能做到的,但微信做到了。研发团队所做的一切都是要给产品决策者有最大的自由度,而这个决策正是微信能够胜出的关键。
敏捷有很多困境,如果做一个单机版程序,是可以做到很敏捷的,但是腾讯正在运作的是一个海量系统,有千万级用户同时在线,在一个单独的功能上每天有百亿级的访问,同时还要保证99.95%的可用性。在海量系统上应对项目开发会有很严谨的规范,都说要尽可能少的变化,因为90%-95%的错误都是在变更中产生的,如果系统一直不变更会获得非常高的稳定度,但是微信就是要在悬崖边跳舞。微信的研发团队要做一些事情,让敏捷开发变得更简单。
如何做到这一切?周颢认为,首先,必须建立起一种狂热的技术信念,就是一定是可以做到的。然后,需要用一些稳固的技术(理念)来支撑,例如大系统小做、让一切可扩展、必须有基础组件、轻松上线(灰度、灰度、再灰度;精细监控;迅速响应)...等等来支撑。
当设计庞大系统的时候,应该尽量分割成更小的颗粒,使得项目之间的影响是最小的。仅仅把模块变得更为清晰,这在海量系统设计开发中是不够的,还需要在物理环境上进行分离部署,出现问题的时候可以快速发现,并且在最快的情况下解决掉。
大系统小做,混搭模式:
将不同的应用逻辑物理分割独立出来,用户注册登录、LBS逻辑、摇一摇逻辑、漂流瓶逻辑、消息逻辑独立开来。把关键的逻辑混搭在一起,当所有的逻辑部署在同一个服务器上,确实也会带来很大敏捷上的好处,因为不需要额外的考虑部署和监控的问题。在整个微信的逻辑中,可能现在已经有上百种不同的逻辑,因为会在逻辑的分割上拆分成8-10种做分离部署。
在高稳定度、高性能的系统中间,为了稳定性能把它设计成不变化的系统,但为了支持敏捷需要让一切的东西都要变得可以扩展。
扩展的关键点有两块。一个是网络协议需要扩展,当要升级一个新功能的时候,会有一些比较大的困难,所以所有协议设计都比较向前兼容,但是向前兼容还是不够的,因为网络协议设计本身有非常多的功能也会有比较大的字段,相关的代码可能会有数千行,这一块不能通过手写方式完成。可以通过XML描述,再通过工具自动生成所有的代码,这是微信获得快速开发的一个重要的点。
另外一块就是在数据存储方面是必须可扩展的。在2005年绝大多数海量系统的设计都是采用固定字段的存储,但是在现代系统中会意识到这个问题,会采用KV或者TLV的方式,微信也做了不同的设计。
把复杂逻辑都固化下来,成为基础软件。在微信后台会有几种不同的基础组件。大致包括:
在变更后的部署方式上,微信在一些规则会限定不能一次把所有的逻辑变更上去,每一次变更一小点观察到每一个环节没有问题的时候,才能布局到全网上去。微信后台每一天可以支撑超过20个后台变更,在业界来说,通常做到5个已经是比较快了,但是微信可以做到快4倍。
腾讯内部的上线系统:
而所谓灰度发布,是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面 来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。(在腾讯,灰度发布是最常采用的发布方式之一)
常识上,解决一个复杂问题的时候,会用高明的技巧解决复杂的问题,这个不是微信团队的目标,他们追求的要做到让所有问题很自然和简单的方式解决掉。在周颢看来,微信架构的技术复杂点在四个要点:协议、容灾、轻重、监控。
微信架构:
在协议设计上,移动互联网和常规互联网有很大的区别。首先有CMWAP和CMNET的不同,在中国现在有相当多的手机用户使用WMWAP连接,还有就是在线和离线的概念,当QQ下线的时候叫离线,当你登录的时候叫在线。但是在移动互联网这两个概念比较模糊。从微信的设计中,不管在线还是离线系统表现都应该是一致的。还有一个是连接不稳定的问题,由于手机信号强弱的变化,当时信号很好,5秒钟走到信号不好的地区,连接就必须断掉。这个中间带来不稳定的因素为协议设计带来较大困难。此外就是资费敏感的问题,因为移动互联网是按照流量计费的,这个计费会使得在协议设计中如何最小化传输的问题。最后就是高延迟的问题。
对此,业界标准的解决方案:Messaging And Presence Protocol:1)XMPP2)SIP/SIMPLE。它的优点是简单,大量开源实现。而缺点同样明显:1)流量大:状态初始化;2)消息不可靠。
微信在系统中做了特殊设计,叫SYNC协议,是参考Activesyec来实现的。特点首先是基于状态同步的协议,假定说收发消息本身是状态同步的过程,假定终端和服务器状态已经被迟了,在服务器端收到最新的消息,当客户端、终端向服务器对接的时候,收取消息的过程实际上可以简单的归纳为状态同步的过程,收消息以及收取你好友状态更新都是相同的。在这样的模式之下,我们会也许会把交互的模式统一化,只需要推送一个消息到达的通知就可以了,终端收到这个通知就来做消息的同步。在这样的简化模式之下,安卓和塞班都可以得到统一。这样的系统本身的实现是更为复杂的,但是获得很多额外的好处。
让剩下系统实现的部分更加简单,简化了交互模式,状态同步可以通过状态同步的差值获得最小的数据变更,通过增量的传输得到最小的数据传输量。通过这样的协议设计,微信可以确保消息是稳定到达的,而且是按序到达。引用一句俗话:比它炫的没它简单,比它简单的没它快,没谁比他更快,哪怕在GPRS下,微信也能把进度条轻易推到底。
周颢介绍了在微信上具体容灾设计的做法。在所有的容灾中存储层的容灾是最难的,一个系统的设计分为三层:接入层、逻辑层、存储层。接入层和逻辑层的容灾都有比较成熟的方案。逻辑层的容灾相对来说比较简单,尽量不要有状态的设计,比如说当你做上一个请求的时候,会保持一些状态,要使得下一个请求发到下一个服务器。如果任何一个请求之间互相不关联的话,这个就是无状态的设计,只要做到这一点逻辑层的容灾可以随意的切换。在回到存储层本身的容灾设计上,相对来说困难一些,但是微信研发团队采用了一些技巧,叫分而治之,分离业务场景,寻求简单的设计,并不会寻求大而同一的解决方案,因为这样会使得系统的复杂度大幅度上升,而微信会尽可能把产品拆细,寻求简化的设计。
首先是主备容灾,这是最常见的方案。在有一些业务场景中是可以容忍最终一致性的,比如账号系统的设计,每天写入账号系统的请求是非常少的,但是访问的请求非常多,这个差异可能会达到数万倍的规模,在这样的场景下,微信会在账号系统中采用简化的方案,也可以获得比较大的稳定度。
SET模型+双写:
第二种容灾的模式叫双写,两台Master的机器,当一台机故障的时候,另外一台机还是可以接收到写请求,当两台机交错启动的时候,会得到数据的丢失。但是有一些场景是可以容忍轻度数据丢失的,比如说会有一个存储专门记录用户终端的类型,比如说安卓还是塞班以及他们使用终端的微信版本是什么,这样的数据是可以容忍轻度数据丢失的,因为偶尔有一些丢失的话,下一次访问会把这些数据带上来,会尽快的修复所有的数据。双写也是非常简单的模式。
微信的研发团队做了一个叫Simple Quorum的机制,在微信的后台中,同步协议有一个很重要的基石叫序列发生器,这样的一个序列发生器需要有极高的稳定度。首先可以看到序列号有一个特点永远是递增的,用递增方式往前推进的时候,最大的序列号就是最新的系列号。有一个毕业才加入广研的毕业生想到一个绝佳的方案,按SET分布,从2G减到200K。
周颢还谈到了轻重的概念。这个概念的提出主要是从终端本身的一些困境所带来的。首先在终端上需要表现最多的一个产品的逻辑,逻辑非常复杂,变更的成本也非常高,当需要修复的时候必须发布一个新版本,这个新版必须由自己下载才能完成,下载的成本非常高。在这样的前提下,如果手机终端产生了任何变化的时候,如果这个变化有非常大的问题就会有极大的困境,所以需要在每一个发布之前做一些充分的数据,确保不会发生致命问题。如果一旦出现致命问题难以修复,需要把关键的点从终端移到后台实现,把功能点后移,来充分发挥后台快速变更的能力。
接入优化:从GSLB到IP重定向
在接入层的优化,速度很重要的因素,是不是能够就近接入一个最优的节点,比如说移动用户最好接入移动的节点,海外的用户可能需要寻找更佳的路由,有的时候可能无法自动做到这一点,一点是在终端上做测速,微信会通过在后台IP逆向的能力,通过后台指挥微信终端联网的能力,寻找最优的接入点。上图就是每分钟收到同一项指令曲线的报表。
如何解决“偷流量”的问题 ——当国内类微信类产品发布的时候出现一个大的问题就是“偷流量”,当用户在某一些逻辑下进行一个死循环,不断访问某一些数据,这样的死循环是非常可怕的,如果在用户不知觉的情况之下,可能会在一个小时之内偷到数10兆甚至数百兆的流量。有非常多业内的同行都需要花大量的精力解决这个问题,微信研发团队用了非常强大的方式解决它。通过在后台建立起严厉的监控系统,对每一个用户的行为做一个监控,当发现异常的时候,后台会给终端发出指令,使得微信终端在一段时间无法联网,但是可以保证用户流量不会白白的使用掉。
功能适配的例子 ——第一期微信版本发布的时候,当时没有群聊的功能,第二版发布的时候做了这个功能。当时有两个选择,对于早期版本的用户,因为不支持群聊,就无法享用到这个功能,但是微信希望提供更好的选择,想让早期不支持群聊的版本,也可以被拉到一个群里面收消息、发消息,通过后台功能的适配也能做到这个事情。
对于一个海量系统来说,一个精密的仪表盘非常重要。监控是非常痛苦的,对于这样一个系统来说,每小时会产生数百G的监控日志。微信希望在1分钟之内监控的数据就能够显示在报表上,因为只有这样的精准和实时度才能够赢得处理故障的时间。微信会做关联统计,通过摇一摇加了好友,他们活跃度如何,过了一段时间他们的活跃度变化情况又是如何。这种需求是需要通过大量日志的关联统计来获得的。研发团队也花了一段时间来理解这个问题,发现了中间一个重要的经验叫做“鱼和熊掌不能兼得”。
为了让监控数值更敏感,需要把监控细化再细化,上面数据表示每一栏子系统的数据,下面这个是按微信版本号来划分的,这里的数据项是非常多。
微信还需要采集一些异常的点,如果有异常的话会发布紧急的版本,尽可能快的替换它。对收发消息延时做的监控,比如说0—1秒端到端的速度,会对不同的区段做一些统计,当某一个环节出现异常的时候,通常会在中间的延时上体现出来。有一个很重要的点叫自动报警,现在有数千项的数据,不可能每一项都靠人工去看的,必须要跟自动报警相关联,微信有一些智能的算法,是不是在正常的范围内,跟 历史 的数值进行对比,如果有异常的话,会通过短信、邮件还有微信本身来发出报警信息。
微信会把监控嵌入到基础框架里面去,因为并不是每一个人都会意识到在需要的地方嵌入一个监控点,所以在基础框架本身内置很重要的监控点,比如说这个表上的栏目,非常多的栏目大概会有数百项的栏目,都不需要程序员自己去写,当用基础组件搭建一个系统的时候,就可以直接观测系统数据。
在谈到微信未来的技术挑战时,周颢首先希望能够让微信成为可用性99.99%的系统;设计出面向现在10倍容量的系统以及完全的IDC容灾。
网上盛传的凌晨两点,腾讯大厦那多层大片大片的灯光和楼下那长长的出租车队伍说明了一切。引用一句话做结尾:“可怕的不是微信,真正可怕的是,比你领先比你更有天赋的团队比你更努力”。
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)