微信后臺系統(tǒng)的演進之路

從無到有:微信后臺系統(tǒng)的演進之路_騰訊大數(shù)據(jù)
http://data.qq.com/article?id=2927

從無到有:微信后臺系統(tǒng)的演進之路_騰訊大數(shù)據(jù)
http://data.qq.com/article?id=2927
從無到有

2011.1.21 微信正式發(fā)布热芹。這一天距離微信項目啟動日約為2個月。就在這2個月里,微信從無到有,大家可能會好奇這期間微信后臺做的最重要的事情是什么?
我想應該是以下三件事:

  1. 確定了微信的消息模型

微信起初定位是一個通訊工具票从,作為通訊工具最核心的功能是收發(fā)消息。微信團隊源于廣硏團隊,消息模型跟郵箱的郵件模型也很有淵源炮姨,都是存儲轉發(fā)。

圖 1 微信消息模型
圖1展示了這一消息模型碰煌,消息被發(fā)出后舒岸,會先在后臺臨時存儲;為使接收者能更快接收到消息芦圾,會推送消息通知給接收者蛾派;最后客戶端主動到服務器收取消息。

  1. 制定了數(shù)據(jù)同步協(xié)議

由于用戶的帳戶、聯(lián)系人和消息等數(shù)據(jù)都在服務器存儲洪乍,如何將數(shù)據(jù)同步到客戶端就成了很關鍵的問題眯杏。為簡化協(xié)議,我們決定通過一個統(tǒng)一的數(shù)據(jù)同步協(xié)議來同步用戶所有的基礎數(shù)據(jù)壳澳。
最初的方案是客戶端記錄一個本地數(shù)據(jù)的快照(Snapshot)岂贩,需要同步數(shù)據(jù)時,將Snapshot帶到服務器巷波,服務器通過計算Snapshot與服務器數(shù)據(jù)的差異萎津,將差異數(shù)據(jù)發(fā)給客戶端,客戶端再保存差異數(shù)據(jù)完成同步抹镊。不過這個方案有兩個問題:一是Snapshot會隨著客戶端數(shù)據(jù)的增多變得越來越大锉屈,同步時流量開銷大;二是客戶端每次同步都要計算Snapshot垮耳,會帶來額外的性能開銷和實現(xiàn)復雜度颈渊。
幾經(jīng)討論后,方案改為由服務計算Snapshot终佛,在客戶端同步數(shù)據(jù)時跟隨數(shù)據(jù)一起下發(fā)給客戶端俊嗽,客戶端無需理解Snapshot,只需存儲起來查蓉,在下次數(shù)據(jù)同步數(shù)據(jù)時帶上即可乌询。同時,Snapshot被設計得非常精簡豌研,是若干個Key-Value的組合妹田,Key代表數(shù)據(jù)的類型,Value代表給到客戶端的數(shù)據(jù)的最新版本號鹃共。Key有三個鬼佣,分別代表:帳戶數(shù)據(jù)、聯(lián)系人和消息霜浴。這個同步協(xié)議的一個額外好處是客戶端同步完數(shù)據(jù)后晶衷,不需要額外的ACK協(xié)議來確認數(shù)據(jù)收取成功,同樣可以保證不會丟數(shù)據(jù):只要客戶端拿最新的Snapshot到服務器做數(shù)據(jù)同步阴孟,服務器即可確認上次數(shù)據(jù)已經(jīng)成功同步完成晌纫,可以執(zhí)行后續(xù)操作,例如清除暫存在服務的消息等等永丝。
此后锹漱,精簡方案、減少流量開銷慕嚷、盡量由服務器完成較復雜的業(yè)務邏輯哥牍、降低客戶端實現(xiàn)的復雜度就作為重要的指導原則毕泌,持續(xù)影響著后續(xù)的微信設計開發(fā)。記得有個比較經(jīng)典的案例是:我們在微信1.2版實現(xiàn)了群聊功能嗅辣,但為了保證新舊版客戶端間的群聊體驗撼泛,我們通過服務器適配,讓1.0版客戶端也能參與群聊澡谭。

  1. 定型了后臺架構

圖 2 微信后臺系統(tǒng)架構
微信后臺使用三層架構:接入層愿题、邏輯層和存儲層。
接入層提供接入服務译暂,包括長連接入服務和短連接入服務抠忘。長連接入服務同時支持客戶端主動發(fā)起請求和服務器主動發(fā)起推送撩炊;短連接入服務則只支持客戶端主動發(fā)起請求外永。
邏輯層包括業(yè)務邏輯服務和基礎邏輯服務。業(yè)務邏輯服務封裝了業(yè)務邏輯拧咳,是后臺提供給微信客戶端調用的API伯顶。基礎邏輯服務則抽象了更底層和通用的業(yè)務邏輯骆膝,提供給業(yè)務邏輯服務訪問祭衩。
存儲層包括數(shù)據(jù)訪問服務和數(shù)據(jù)存儲服務。數(shù)據(jù)存儲服務通過MySQL和SDB(廣硏早期后臺中廣泛使用的Key-Table數(shù)據(jù)存儲系統(tǒng))等底層存儲系統(tǒng)來持久化用戶數(shù)據(jù)阅签。數(shù)據(jù)訪問服務適配并路由數(shù)據(jù)訪問請求到不同的底層數(shù)據(jù)存儲服務掐暮,面向邏輯層提供結構化的數(shù)據(jù)服務。比較特別的是政钟,微信后臺每一種不同類型的數(shù)據(jù)都使用單獨的數(shù)據(jù)訪問服務和數(shù)據(jù)存儲服務路克,例如帳戶、消息和聯(lián)系人等等都是獨立的养交。
微信后臺主要使用C++精算。后臺服務使用Svrkit框架搭建,服務之間通過同步RPC進行通訊碎连。

圖 3 Svrkit 框架
Svrkit是另一個廣硏后臺就已經(jīng)存在的高性能RPC框架灰羽,當時尚未廣泛使用,但在微信后臺卻大放異彩鱼辙。作為微信后臺基礎設施中最重要的一部分廉嚼,Svrkit這幾年一直不斷在進化。我們使用Svrkit構建了數(shù)以千計的服務模塊倒戏,提供數(shù)萬個服務接口怠噪,每天RPC調用次數(shù)達幾十萬億次。
這三件事影響深遠峭梳,乃至于5年后的今天舰绘,我們仍繼續(xù)沿用最初的架構和協(xié)議蹂喻,甚至還可以支持當初1.0版的微信客戶端。
這里有一個經(jīng)驗教訓——運營支撐系統(tǒng)真的很重要捂寿。第一個版本的微信后臺是倉促完成的口四,當時只是完成了基礎業(yè)務功能,并沒有配套的業(yè)務數(shù)據(jù)統(tǒng)計等等秦陋。我們在開放注冊后蔓彩,一時間竟沒有業(yè)務監(jiān)控頁面和數(shù)據(jù)曲線可以看,注冊用戶數(shù)是臨時從數(shù)據(jù)庫統(tǒng)計的驳概,在線數(shù)是從日志里提取出來的赤嚼,這些數(shù)據(jù)通過每個小時運行一次的腳本(這個腳本也是當天臨時加的)統(tǒng)計出來,然后自動發(fā)郵件到郵件組顺又。還有其他各種業(yè)務數(shù)據(jù)也通過郵件進行發(fā)布更卒,可以說郵件是微信初期最重要的數(shù)據(jù)門戶。
2011.1.21 當天最高并發(fā)在線數(shù)是 491稚照,而今天這個數(shù)字是4億蹂空。
小步慢跑

在微信發(fā)布后的4個多月里,我們經(jīng)歷了發(fā)布后火爆注冊的驚喜果录,也經(jīng)歷了隨后一直不溫不火的困惑上枕。
這一時期,微信做了很多旨在增加用戶好友量弱恒,讓用戶聊得起來的功能辨萍。打通騰訊微博私信、群聊返弹、工作郵箱锈玉、QQ/郵箱好友推薦等等。對于后臺而言琉苇,比較重要的變化就是這些功能催生了對異步隊列的需求嘲玫。例如,微博私信需要跟外部門對接并扇,不同系統(tǒng)間的處理耗時和速度不一樣去团,可以通過隊列進行緩沖;群聊是耗時操作穷蛹,消息發(fā)到群后土陪,可以通過異步隊列來異步完成消息的擴散寫等等。

圖 4 單聊和群聊消息發(fā)送過程
圖4是異步隊列在群聊中的應用肴熏。微信的群聊是寫擴散的鬼雀,也就是說發(fā)到群里的一條消息會給群里的每個人都存一份(消息索引)。為什么不是讀擴散呢蛙吏?有兩個原因:
群的人數(shù)不多源哩,群人數(shù)上限是10(后來逐步加到20鞋吉、40、100励烦,目前是500)谓着,擴散的成本不是太大,不像微博坛掠,有成千上萬的粉絲赊锚,發(fā)一條微博后,每粉絲都存一份的話屉栓,一個是效率太低舷蒲,另一個存儲量也會大很多;
消息擴散寫到每個人的消息存儲(消息收件箱)后友多,接收者到后臺同步數(shù)據(jù)時牲平,只需要檢查自己收件箱即可,同步邏輯跟單聊消息是一致的夷陋,這樣可以統(tǒng)一數(shù)據(jù)同步流程欠拾,實現(xiàn)起來也會很輕量胰锌。
異步隊列作為后臺數(shù)據(jù)交互的一種重要模式骗绕,成為了同步RPC服務調用之外的有力補充,在微信后臺被大量使用资昧。
快速成長

微信的飛速發(fā)展是從2.0版開始的酬土,這個版本發(fā)布了語音聊天功能。之后微信用戶量急速增長格带,2011.5用戶量破100萬撤缴、2011.7 用戶量破1000萬、2012.3 注冊用戶數(shù)突破1億叽唱。
伴隨著喜人成績而來的屈呕,還有一堆幸福的煩惱。
業(yè)務快速迭代的壓力
微信發(fā)布時功能很簡單棺亭,主要功能就是發(fā)消息虎眨。不過在發(fā)語音之后的幾個版本里迅速推出了手機通訊錄、QQ離線消息镶摘、查看附近的人嗽桩、搖一搖、漂流瓶和朋友圈等等功能凄敢。
有個廣為流傳的關于朋友圈開發(fā)的傳奇——朋友圈歷經(jīng)4個月碌冶,前后做了30多個版本迭代才最終成型。其實還有一個鮮為人知的故事——那時候因為人員比較短缺涝缝,朋友圈后臺長時間只有1位開發(fā)人員扑庞。
后臺穩(wěn)定性的要求
用戶多了譬重,功能也多了,后臺模塊數(shù)和機器量在不斷翻番罐氨,緊跟著的還有各種故障害幅。
幫助我們順利度過這個階段的,是以下幾個舉措:

  1. 極簡設計

雖然各種需求撲面而來岂昭,但我們每個實現(xiàn)方案都是一絲不茍完成的以现。實現(xiàn)需求最大的困難不是設計出一個方案并實現(xiàn)出來,而是需要在若干個可能的方案中约啊,甄選出最簡單實用的那個邑遏。
這中間往往需要經(jīng)過幾輪思考——討論——推翻的迭代過程,謀定而后動有不少好處恰矩,一方面可以避免做出華而不實的過度設計记盒,提升效率;另一方面外傅,通過詳盡的討論出來的看似簡單的方案纪吮,細節(jié)考究,往往是可靠性最好的方案萎胰。

  1. 大系統(tǒng)小做

邏輯層的業(yè)務邏輯服務最早只有一個服務模塊(我們稱之為mmweb)碾盟,囊括了所有提供給客戶端訪問的API,甚至還有一個完整的微信官網(wǎng)技竟。這個模塊架構類似Apache冰肴,由一個CGI容器(CGIHost)和若干CGI組成(每個CGI即為一個API),不同之處在于每個CGI都是一個動態(tài)庫so榔组,由CGIHost動態(tài)加載熙尉。
在mmweb的CGI數(shù)量相對較少的時候,這個模塊的架構完全能滿足要求搓扯,但當功能迭代加快检痰,CGI量不斷增多之后,開始出現(xiàn)問題:

  1. 每個CGI都是動態(tài)庫锨推,在某些CGI的共用邏輯的接口定義發(fā)生變化時铅歼,不同時期更新上線的CGI可能使用了不同版本的邏輯接口定義,會導致在運行時出現(xiàn)詭異結果或者進程crash爱态,而且非常難以定位谭贪;
  2. 所有CGI放在一起,每次大版本發(fā)布上線锦担,從測試到灰度再到全面部署完畢俭识,都是一個很漫長的過程,幾乎所有后臺開發(fā)人員都會被同時卡在這個環(huán)節(jié)洞渔,非常影響效率套媚;
  3. 新增的不太重要的CGI有時穩(wěn)定性不好缚态,某些異常分支下會crash,導致CGIHost進程無法服務堤瘤,發(fā)消息這些重要CGI受影響沒法運行玫芦。
    于是我們開始嘗試使用一種新的CGI架構——Logicsvr。
    Logicsvr基于Svrkit框架本辐。將Svrkit框架和CGI邏輯通過靜態(tài)編譯生成可直接使用HTTP訪問的Logicsvr桥帆。我們將mmweb模塊拆分為8個不同服務模塊。拆分原則是:實現(xiàn)不同業(yè)務功能的CGI被拆到不同Logicsvr慎皱,同一功能但是重要程度不一樣的也進行拆分老虫。例如,作為核心功能的消息收發(fā)邏輯茫多,就被拆為3個服務模塊:消息同步祈匙、發(fā)文本和語音消息、發(fā)圖片和視頻消息天揖。
    每個Logicsvr都是一個獨立的二進制程序夺欲,可以分開部署、獨立上線今膊。時至今日些阅,微信后臺有數(shù)十個Logicsvr,提供了數(shù)百個CGI服務万细,部署在數(shù)千臺服務器上扑眉,每日客戶端訪問量幾千億次。
    除了API服務外赖钞,其他后臺服務模塊也遵循“大系統(tǒng)小做”這一實踐準則,微信后臺服務模塊數(shù)從微信發(fā)布時的約10個模塊聘裁,迅速上漲到數(shù)百個模塊雪营。
  1. 業(yè)務監(jiān)控

這一時期,后臺故障很多衡便。比故障更麻煩的是献起,因為監(jiān)控的缺失,經(jīng)常有些故障我們沒法第一時間發(fā)現(xiàn)镣陕,造成故障影響面被放大谴餐。
監(jiān)控的缺失一方面是因為在快速迭代過程中,重視功能開發(fā)呆抑,輕視了業(yè)務監(jiān)控的重要性岂嗓,有故障一直是兵來將擋水來土掩;另一方面是基礎設施對業(yè)務邏輯監(jiān)控的支持度較弱鹊碍⊙嵫常基礎設施提供了機器資源監(jiān)控和Svrkit服務運行狀態(tài)的監(jiān)控食绿。這個是每臺機器、每個服務標配的公罕,無需額外開發(fā)器紧,但是業(yè)務邏輯的監(jiān)控就要麻煩得多了。當時的業(yè)務邏輯監(jiān)控是通過業(yè)務邏輯統(tǒng)計功能來做的楼眷,實現(xiàn)一個監(jiān)控需要4步:

  1. 申請日志上報資源铲汪;
  2. 在業(yè)務邏輯中加入日志上報點,日志會被每臺機器上的agent收集并上傳到統(tǒng)計中心罐柳;
  3. 開發(fā)統(tǒng)計代碼桥状;
  4. 實現(xiàn)統(tǒng)計監(jiān)控頁面。
    可以想象硝清,這種費時費力的模式會反過來降低開發(fā)人員對加入業(yè)務監(jiān)控的積極性辅斟。于是有一天,我們去公司內的標桿——即通后臺(QQ后臺)取經(jīng)了芦拿,發(fā)現(xiàn)解決方案出乎意料地簡單且強大:
  5. 故障報告
    之前每次故障后士飒,是由QA牽頭出一份故障報告,著重點是對故障影響的評估和故障定級蔗崎。新的做法是每個故障不分大小酵幕,開發(fā)人員需要徹底復盤故障過程,然后商定解決方案缓苛,補充出一份詳細的技術報告芳撒。這份報告?zhèn)戎赜冢喝绾伪苊馔愋凸收显俅伟l(fā)生、提高故障主動發(fā)現(xiàn)能力未桥、縮短故障響應和處理過程笔刹。
  6. 基于 ID-Value 的業(yè)務無關的監(jiān)控告警體系

圖 5 基于 ID-Value 的監(jiān)控告警體系
監(jiān)控體系實現(xiàn)思路非常簡單,提供了2個API冬耿,允許業(yè)務代碼在共享內存中對某個監(jiān)控ID進行設置Value或累加Value的功能舌菜。每臺機器上的Agent會定時將所有ID-Value上報到監(jiān)控中心,監(jiān)控中心對數(shù)據(jù)匯總入庫后就可以通過統(tǒng)一的監(jiān)控頁面輸出監(jiān)控曲線亦镶,并通過預先配置的監(jiān)控規(guī)則產(chǎn)生報警日月。
對于業(yè)務代碼來說,只需在要被監(jiān)控的業(yè)務流程中調用一下監(jiān)控API缤骨,并配置好告警條件即可爱咬。這就極大地降低了開發(fā)監(jiān)控報警的成本,我們補全了各種監(jiān)控項绊起,讓我們能主動及時地發(fā)現(xiàn)問題精拟。新開發(fā)的功能也會預先加入相關監(jiān)控項,以便在少量灰度階段就能直接通過監(jiān)控曲線了解業(yè)務是否符合預期。

  1. KVSvr

微信后臺每個存儲服務都有自己獨立的存儲模塊串前,是相互獨立的瘫里。每個存儲服務都有一個業(yè)務訪問模塊和一個底層存儲模塊組成。業(yè)務訪問層隔離業(yè)務邏輯層和底層存儲荡碾,提供基于RPC的數(shù)據(jù)訪問接口谨读;底層存儲有兩類:SDB和MySQL。
SDB適用于以用戶UIN(uint32_t)為Key的數(shù)據(jù)存儲坛吁,比方說消息索引和聯(lián)系人劳殖。優(yōu)點是性能高,在可靠性上拨脉,提供基于異步流水同步的Master-Slave模式哆姻,Master故障時,Slave可以提供讀數(shù)據(jù)服務玫膀,無法寫入新數(shù)據(jù)矛缨。
由于微信賬號為字母+數(shù)字組合,無法直接作為SDB的Key帖旨,所以微信帳號數(shù)據(jù)并非使用SDB箕昭,而是用MySQL存儲的。MySQL也使用基于異步流水復制的Master-Slave模式解阅。
第1版的帳號存儲服務使用Master-Slave各1臺落竹。Master提供讀寫功能,Slave不提供服務货抄,僅用于備份述召。當Master有故障時,人工切讀服務到Slave蟹地,無法提供寫服務积暖。為提升訪問效率,我們還在業(yè)務訪問模塊中加入了memcached提供Cache服務锈津,減少對底層存儲訪問呀酸。
第2版的帳號存儲服務還是Master-Slave各1臺,區(qū)別是Slave可以提供讀服務琼梆,但有可能讀到臟數(shù)據(jù),因此對一致性要求高的業(yè)務邏輯窿吩,例如注冊和登錄邏輯只允許訪問Master茎杂。當Master有故障時,同樣只能提供讀服務纫雁,無法提供寫服務煌往。
第3版的帳號存儲服務采用1個Master和多個Slave,解決了讀服務的水平擴展能力。
第4版的帳號服務底層存儲采用多個Master-Slave組刽脖,每組由1個Master和多個Slave組成羞海,解決了寫服務能力不足時的水平擴展能力。
最后還有個未解決的問題:單個Master-Slave分組中曲管,Master還是單點却邓,無法提供實時的寫容災,也就意味著無法消除單點故障院水。另外Master-Slave的流水同步延時對讀服務有很大影響腊徙,流水出現(xiàn)較大延時會導致業(yè)務故障。于是我們尋求一個可以提供高性能檬某、具備讀寫水平擴展撬腾、沒有單點故障、可同時具備讀寫容災能力恢恼、能提供強一致性保證的底層存儲解決方案民傻,最終KVSvr應運而生。
KVSvr使用基于Quorum的分布式數(shù)據(jù)強一致性算法场斑,提供Key-Value/Key-Table模型的存儲服務漓踢。傳統(tǒng)Quorum算法的性能不高,KVSvr創(chuàng)造性地將數(shù)據(jù)的版本和數(shù)據(jù)本身做了區(qū)分和簸,將Quorum算法應用到數(shù)據(jù)的版本的協(xié)商彭雾,再通過基于流水同步的異步數(shù)據(jù)復制提供了數(shù)據(jù)強一致性保證和極高的數(shù)據(jù)寫入性能,另外KVSvr天然具備數(shù)據(jù)的Cache能力锁保,可以提供高效的讀取性能薯酝。
KVSvr一舉解決了我們當時迫切需要的無單點故障的容災能力。除了第5版的帳號服務外爽柒,很快所有SDB底層存儲模塊和大部分MySQL底層存儲模塊都切換到KVSvr吴菠。隨著業(yè)務的發(fā)展,KVSvr也不斷在進化著浩村,還配合業(yè)務需要衍生出了各種定制版本∽隹現(xiàn)在的KVSvr仍然作為核心存儲,發(fā)揮著舉足輕重的作用心墅。
平臺化

2011.8 深圳舉行大運會酿矢。微信推出“微信深圳大運志愿者服務中心”服務號,微信用戶可以搜索“szdy”將這個服務號加為好友怎燥,獲取大會相關的資訊瘫筐。當時后臺對“szdy”做了特殊處理,用戶搜索時铐姚,會隨機返回“szdy01”策肝,“szdy02”,…,“szdy10”這10個微信號中的1個之众,每個微信號背后都有一個志愿者在服務拙毫。
2011.9 “微成都”落戶微信平臺,微信用戶可以搜索“wechengdu”加好友棺禾,成都市民還可以在“附近的人”看到這個號缀蹄,我們在后臺給這個帳號做了一些特殊邏輯,可以支持后臺自動回復用戶發(fā)的消息帘睦。
這種需求越來越多袍患,我們就開始做一個媒體平臺,這個平臺后來從微信后臺分出竣付,演變成了微信公眾平臺瞬逊,獨立發(fā)展壯大惋增,開始了微信的平臺化之路燥滑。除微信公眾平臺外纺弊,微信后臺的外圍還陸續(xù)出現(xiàn)了微信支付平臺、硬件平臺等等一系列平臺逸绎。

圖 6 微信平臺
走出國門

微信走出國門的嘗試開始于3.0版本惹恃。從這個版本開始,微信逐步支持繁體棺牧、英文等多種語言文字巫糙。不過,真正標志性的事情是第一個海外數(shù)據(jù)中心的投入使用颊乘。

  1. 海外數(shù)據(jù)中心

海外數(shù)據(jù)中心的定位是一個自治的系統(tǒng)参淹,也就是說具備完整的功能,能夠不依賴于國內數(shù)據(jù)中心獨立運作乏悄。

  1. 多數(shù)據(jù)中心架構

圖 7 多數(shù)據(jù)中心架構
系統(tǒng)自治對于無狀態(tài)的接入層和邏輯層來說很簡單浙值,所有服務模塊在海外數(shù)據(jù)中心部署一套就行了。
但是存儲層就有很大麻煩了——我們需要確保國內數(shù)據(jù)中心和海外數(shù)據(jù)中心能獨立運作檩小,但不是兩套隔離的系統(tǒng)各自部署开呐,各玩各的,而是一套業(yè)務功能可以完全互通的系統(tǒng)规求。因此我們的任務是需要保證兩個數(shù)據(jù)中心的數(shù)據(jù)一致性筐付,另外Master-Master架構是個必選項,也即兩個數(shù)據(jù)中心都需要可寫阻肿。

  1. Master-Master 存儲架構
    Master-Master架構下數(shù)據(jù)的一致性是個很大的問題家妆。兩個數(shù)據(jù)中心之間是個高延時的網(wǎng)絡,意味著在數(shù)據(jù)中心之間直接使用Paxos算法冕茅、或直接部署基于Quorum的KVSvr等看似一勞永逸的方案不適用。
    最終我們選擇了跟Yahoo!的PNUTS系統(tǒng)類似的解決方案,需要對用戶集合進行切分姨伤,國內用戶以國內上海數(shù)據(jù)中心為Master哨坪,所有數(shù)據(jù)寫操作必須回到國內數(shù)據(jù)中心完成;海外用戶以海外數(shù)據(jù)中心為Master乍楚,寫操作只能在海外數(shù)據(jù)中心進行当编。從整體存儲上看,這是一個Master-Master的架構徒溪,但細到一個具體用戶的數(shù)據(jù)忿偷,則是Master-Slave模式,每條數(shù)據(jù)只能在用戶歸屬的數(shù)據(jù)中心可寫臊泌,再異步復制到其他數(shù)據(jù)中心鲤桥。

圖 8 多數(shù)據(jù)中心的數(shù)據(jù)Master-Master架構

  1. 數(shù)據(jù)中心間的數(shù)據(jù)一致性
    這個Master-Master架構可以在不同數(shù)據(jù)中心間實現(xiàn)數(shù)據(jù)最終一致性。如何保證業(yè)務邏輯在這種數(shù)據(jù)弱一致性保證下不會出現(xiàn)問題渠概?
    這個問題可以被分解為2個子問題:
    用戶訪問自己的數(shù)據(jù)
    用戶可以滿世界跑茶凳,那是否允許用戶就近接入數(shù)據(jù)中心就對業(yè)務處理流程有很大影響。如果允許就近接入播揪,同時還要保證數(shù)據(jù)一致性不影響業(yè)務贮喧,就意味著要么用戶數(shù)據(jù)的Master需要可以動態(tài)的改變;要么需要對所有業(yè)務邏輯進行仔細梳理猪狈,嚴格區(qū)分本數(shù)據(jù)中心和跨數(shù)據(jù)中心用戶的請求箱沦,將請求路由到正確的數(shù)據(jù)中心處理。
    考慮到上述問題會帶來很高昂的實現(xiàn)和維護的復雜度雇庙,我們限制了每個用戶只能接入其歸屬數(shù)據(jù)中心進行操作谓形。如果用戶發(fā)生漫游,其漫游到的數(shù)據(jù)中心會自動引導用戶重新連回歸屬數(shù)據(jù)中心状共。
    這樣用戶訪問自己數(shù)據(jù)的一致性問題就迎刃而解了套耕,因為所有操作被限制在歸屬數(shù)據(jù)中心內,其數(shù)據(jù)是有強一致性保證的峡继。此外冯袍,還有額外的好處:用戶自己的數(shù)據(jù)(如:消息和聯(lián)系人等)不需要在數(shù)據(jù)中心間同步,這就大大降低了對數(shù)據(jù)同步的帶寬需求碾牌。
    用戶訪問其他用戶的數(shù)據(jù)
    由于不同數(shù)據(jù)中心之間業(yè)務需要互通康愤,用戶會使用到其他數(shù)據(jù)中心用戶創(chuàng)建的數(shù)據(jù)。例如舶吗,參與其他數(shù)據(jù)中心用戶創(chuàng)建的群聊征冷,查看其他數(shù)據(jù)中心用戶的朋友圈等。
    仔細分析后可以發(fā)現(xiàn)誓琼,大部分場景下對數(shù)據(jù)一致性要求其實并不高检激。用戶稍遲些才見到自己被加入某個其他數(shù)據(jù)中心用戶建的群肴捉、稍遲些才見到某個好友的朋友圈動態(tài)更新其實并不會帶來什么問題。在這些場景下叔收,業(yè)務邏輯直接訪問本數(shù)據(jù)中心的數(shù)據(jù)齿穗。
    當然,還是有些場景對數(shù)據(jù)一致性要求很高饺律。比方說給自己設置微信號窃页,而微信號是需要在整個微信帳號體系里保證唯一的。我們提供了全局唯一的微信號申請服務來解決這一問題复濒,所有數(shù)據(jù)中心通過這個服務申請微信號脖卖。這種需要特殊處置的場景極少,不會帶來太大問題巧颈。
  2. 可靠的數(shù)據(jù)同步
    數(shù)據(jù)中心之間有大量的數(shù)據(jù)同步畦木,數(shù)據(jù)是否能夠達到最終一致,取決于數(shù)據(jù)同步是否可靠洛二。為保證數(shù)據(jù)同步的可靠性馋劈,提升同步的可用性,我們又開發(fā)一個基于Quorum算法的隊列組件晾嘶,這個組件的每一組由3機存儲服務組成妓雾。與一般隊列的不同之處在于,這個組件對隊列寫入操作進行了大幅簡化垒迂,3機存儲服務不需要相互通訊械姻,每個機器上的數(shù)據(jù)都是順序寫,執(zhí)行寫操作時在3機能寫入成功2份即為寫入成功机断;若失敗楷拳,則換另外一組再試。因此這個隊列可以達到極高的可用性和寫入性能吏奸。每個數(shù)據(jù)中心將需要同步的數(shù)據(jù)寫入本數(shù)據(jù)中心的同步隊列后欢揖,由其他數(shù)據(jù)中心的數(shù)據(jù)重放服務將數(shù)據(jù)拉走并進行重放,達到數(shù)據(jù)同步的目的奋蔚。
  1. 網(wǎng)絡加速

海外數(shù)據(jù)中心建設周期長她混,投入大,微信只在香港和加拿大有兩個海外數(shù)據(jù)中心泊碑。但世界那么大坤按,即便是這兩個數(shù)據(jù)中心,也還是沒法輻射全球馒过,讓各個角落的用戶都能享受到暢快的服務體驗臭脓。
通過在海外實際對比測試發(fā)現(xiàn),微信客戶端在發(fā)消息等一些主要使用場景與主要競品有不小的差距腹忽。為此来累,我們跟公司的架構平臺部砚作、網(wǎng)絡平臺部和國際業(yè)務部等兄弟部門一起合作,圍繞海外數(shù)據(jù)中心佃扼,在世界各地精心選址建設了數(shù)十個POP點(包括信令加速點和圖片CDN網(wǎng)絡)偎巢。另外,通過對移動網(wǎng)絡的深入分析和研究兼耀,我們還對微信的通訊協(xié)議做了大幅優(yōu)化。微信最終在對比測試中趕上并超過了主要的競品求冷。
精耕細作

  1. 三園區(qū)容災

2013.7.22 微信發(fā)生了有史以來最大規(guī)模的故障瘤运,消息收發(fā)和朋友圈等服務出現(xiàn)長達5個小時的故障,故障期間消息量跌了一半匠题。故障的起因是上海數(shù)據(jù)中心一個園區(qū)的主光纖被挖斷拯坟,近2千臺服務器不可用,引發(fā)整個上海數(shù)據(jù)中心(當時國內只有這一個數(shù)據(jù)中心)的服務癱瘓韭山。
故障時郁季,我們曾嘗試把接入到故障園區(qū)的用戶切走,但收效甚微钱磅。雖然數(shù)百個在線模塊都做了容災和冗余設計梦裂,單個服務模塊看起來沒有單點故障問題;但整體上看盖淡,無數(shù)個服務實例散布在數(shù)據(jù)中心各個機房的8千多臺服務器內年柠,各服務RPC調用復雜,呈網(wǎng)狀結構褪迟,再加上缺乏系統(tǒng)級的規(guī)劃和容災驗證冗恨,最終導致故障無法主動恢復。在此之前味赃,我們知道單個服務出現(xiàn)單機故障不影響系統(tǒng)掀抹,但沒人知道2千臺服務器同時不可用時,整個系統(tǒng)會出現(xiàn)什么不可控的狀況心俗。
其實在這個故障發(fā)生之前3個月傲武,我們已經(jīng)在著手解決這個問題。當時上海數(shù)據(jù)中心內網(wǎng)交換機異常另凌,導致微信出現(xiàn)一個出乎意料的故障谱轨,在13分鐘的時間里,微信消息收發(fā)幾乎完全不可用吠谢。在對故障進行分析時土童,我們發(fā)現(xiàn)一個消息系統(tǒng)里一個核心模塊三個互備的服務實例都部署在同一機房。該機房的交換機故障導致這個服務整體不可用工坊,進而消息跌零献汗。這個服務模塊是最早期(那個時候微信后臺規(guī)模小敢订,大部分后臺服務都部署在一個數(shù)據(jù)園區(qū)里)的核心模塊,服務基于3機冗余設計罢吃,年復一年可靠地運行著楚午,以至于大家都完全忽視了這個問題。
為解決類似問題尿招,三園區(qū)容災應運而生矾柜,目標是將上海數(shù)據(jù)中心的服務均勻部署到3個物理上隔離的數(shù)據(jù)園區(qū),在任意單一園區(qū)整體故障時就谜,微信仍能提供無損服務怪蔑。

  1. 同時服務
    傳統(tǒng)的數(shù)據(jù)中心級災備方案是“兩地三中心”,即同城有兩個互備的數(shù)據(jù)中心丧荐,異地再建設一個災備中心缆瓣,這三個數(shù)據(jù)中心平時很可能只有一個在提供在線服務,故障時再將業(yè)務流量切換到其他數(shù)據(jù)中心虹统。這里的主要問題是災備數(shù)據(jù)中心無實際業(yè)務流量弓坞,在主數(shù)據(jù)中心故障時未必能正常切換到災備中心,并且在平時大量的備份資源不提供服務车荔,也會造成大量的資源浪費渡冻。
    三園區(qū)容災的核心是三個數(shù)據(jù)園區(qū)同時提供服務,因此即便某個園區(qū)整體故障夸赫,那另外兩個園區(qū)的業(yè)務流量也只會各增加50%菩帝。反過來說,只需讓每個園區(qū)的服務器資源跑在容量上限的2/3茬腿,保留1/3的容量即可提供無損的容災能力呼奢,而傳統(tǒng)“兩地三中心”則有多得多的服務器資源被閑置。此外切平,在平時三個園區(qū)同時對外服務握础,因此我們在故障時,需要解決的問題是“怎樣把業(yè)務流量切到其他數(shù)據(jù)園區(qū)悴品?”禀综,而不是“能不能把業(yè)務流量切到其他數(shù)據(jù)園區(qū)?”苔严,前者顯然是更容易解決的一個問題定枷。
  2. 數(shù)據(jù)強一致
    三園區(qū)容災的關鍵是存儲模塊需要把數(shù)據(jù)均勻分布在3個數(shù)據(jù)園區(qū),同一份數(shù)據(jù)要在不同園區(qū)有2個以上的一致的副本届氢,這樣才能保證任意單一園區(qū)出災后欠窒,可以不中斷地提供無損服務。由于后臺大部分存儲模塊都使用KVSvr退子,這樣解決方案也相對簡單高效——將KVSvr的每1組機器都均勻部署在3個園區(qū)里岖妄。
  3. 故障時自動切換
    三園區(qū)容災的另一個難點是對故障服務的自動屏蔽和自動切換型将。即要讓業(yè)務邏輯服務模塊能準確識別出某些下游服務實例已經(jīng)無法訪問,然后迅速自動切到其他服務實例荐虐,避免被拖死七兜。我們希望每個業(yè)務邏輯服務可以在不借助外部輔助信息(如建設中心節(jié)點,由中心節(jié)點下發(fā)各個業(yè)務邏輯服務的健康狀態(tài))的情況下福扬,能自行決策迅速屏蔽掉有問題的服務實例腕铸,自動把業(yè)務流量分散切到其他服務實例上。另外忧换,我們還建設了一套手工操作的全局屏蔽系統(tǒng)恬惯,可以在大型網(wǎng)絡故障時,由人工介入屏蔽掉某個園區(qū)所有的機器亚茬,迅速將業(yè)務流量分散到其他兩個數(shù)據(jù)園區(qū)。
  4. 容災效果檢驗
    三園區(qū)容災是否能正常發(fā)揮作用還需要進行實際的檢驗浓恳,我們在上海數(shù)據(jù)中心和海外的香港數(shù)據(jù)中心完成三園區(qū)建設后刹缝,進行了數(shù)次實戰(zhàn)演習,屏蔽單一園區(qū)上千臺服務颈将,檢驗容災效果是否符合預期梢夯。特別地,為了避免隨著時間的推移某個核心服務模塊因為某次更新就不再支持三園區(qū)容災了晴圾,我們還搭建了一套容災撥測系統(tǒng)颂砸,每天對所有服務模塊選取某個園區(qū)的服務主動屏蔽掉,自動檢查服務整體失敗量是否發(fā)生變化死姚,實現(xiàn)對三園區(qū)容災效果的持續(xù)檢驗人乓。
  1. 性能優(yōu)化

之前我們在業(yè)務迅速發(fā)展之時,優(yōu)先支撐業(yè)務功能快速迭代都毒,性能問題無暇兼顧色罚,比較粗放的貫徹了“先扛住再優(yōu)化”的海量之道。2014年開始大幅縮減運營成本账劲,性能優(yōu)化就被提上了日程戳护。
我們基本上對大部分服務模塊的設計和實現(xiàn)都進行了重新review,并進行了有針對性的優(yōu)化瀑焦,這還是可以節(jié)約出不少機器資源的腌且。但更有效的優(yōu)化措施是對基礎設施的優(yōu)化,具體的說是對Svrkit框架的優(yōu)化榛瓮。Svrkit框架被廣泛應用到幾乎所有服務模塊铺董,如果框架層面能把機器資源使用到極致,那肯定是事半功倍的榆芦。
結果還真的可以柄粹,我們在基礎設施里加入了對協(xié)程的支持喘鸟,重點是這個協(xié)程組件可以不破壞原來的業(yè)務邏輯代碼結構,讓我們原有代碼中使用同步RPC調用的代碼不做任何修改驻右,就可以直接通過協(xié)程異步化什黑。Svrkit框架直接集成了這個協(xié)程組件,然后美好的事情發(fā)生了堪夭,原來單實例最多提供上百并發(fā)請求處理能力的服務愕把,在重編上線后,轉眼間就能提供上千并發(fā)請求處理能力森爽。Svrkit框架的底層實現(xiàn)在這一時期也做了全新的實現(xiàn)恨豁,服務的處理能力大幅提高。

  1. 防雪崩

我們一直以來都不太擔心某個服務實例出現(xiàn)故障爬迟,導致這個實例完全無法提供服務的問題橘蜜,這個在后臺服務的容災體系里可以被處理得很好。最擔心的是雪崩:某個服務因為某些原因出現(xiàn)過載付呕,導致請求處理時間被大大拉長计福。于是服務吞吐量下降,大量請求積壓在服務的請求隊列太長時間了徽职,導致訪問這個服務的上游服務出現(xiàn)超時象颖。更倒霉的是上游服務還經(jīng)常會重試,然后這個過載的服務僅有的一點處理能力都在做無用功(即處理完畢返回結果時姆钉,調用端都已超時放棄)说订,終于這個過載的服務徹底雪崩了。最糟糕的情況是上游服務每個請求都耗時那么久潮瓶,雪崩順著RPC調用鏈一級級往上傳播陶冷,最終單個服務模塊的過載會引發(fā)大批服務模塊的雪崩。
我們在一番勒緊褲腰帶節(jié)省機器資源筋讨、消滅低負載機器后埃叭,所有機器的負載都上來了,服務過載變得經(jīng)常發(fā)生了悉罕。解決這一問題的有力武器是Svrkit框架里的具有QoS保障的FastReject機制赤屋,可以快速拒絕掉超過服務自身處理能力的請求,即使在過載時壁袄,也能穩(wěn)定地提供有效輸出类早。

  1. 安全加固

近年,互聯(lián)網(wǎng)安全事件時有發(fā)生嗜逻,各種拖庫層出不窮涩僻。為保護用戶的隱私數(shù)據(jù),我們建設了一套數(shù)據(jù)保護系統(tǒng)——全程票據(jù)系統(tǒng)。其核心方案是逆日,用戶登錄后嵌巷,后臺會下發(fā)一個票據(jù)給客戶端,客戶端每次請求帶上票據(jù)室抽,請求在后臺服務的整個處理鏈條中搪哪,所有對核心數(shù)據(jù)服務的訪問,都會被校驗票據(jù)是否合法坪圾,非法請求會被拒絕晓折,從而保障用戶隱私數(shù)據(jù)只能用戶通過自己的客戶端發(fā)起操作來訪問。
新的挑戰(zhàn)

  1. 資源調度系統(tǒng)

微信后臺有成千的服務模塊兽泄,部署在全球數(shù)以萬計的服務器上漓概,一直依靠人工管理。此外病梢,微信后臺主要是提供實時在線服務胃珍,每天的服務器資源占用在業(yè)務高峰和低谷時相差很大,在業(yè)務低谷時計算資源被白白浪費蜓陌;另一方面堂鲜,很多離線的大數(shù)據(jù)計算卻受制于計算資源不足,難以高效完成护奈。
我們正在實驗和部署的資源調度系統(tǒng)(Yard)可以把機器資源的分配和服務的部署自動化、把離線任務的調度自動化哥纫,實現(xiàn)了資源的優(yōu)化配置霉旗,在業(yè)務對服務資源的需求有變化時,能更及時蛀骇、更彈性地自動實現(xiàn)服務的重新配置與部署厌秒。

  1. 高可用存儲

基于Quorum算法的KVSvr已經(jīng)實現(xiàn)了強一致性、高可用且高性能的Key-Value/Key-Table存儲擅憔。最近鸵闪,微信后臺又誕生了基于Paxos算法的另一套存儲系統(tǒng),首先落地的是PhxSQL暑诸,一個支持完整MySQL功能蚌讼,又同時具備強一致性、高可用和高性能的SQL存儲个榕。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末篡石,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子西采,更是在濱河造成了極大的恐慌凰萨,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胖眷,居然都是意外死亡武通,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門珊搀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冶忱,“玉大人,你說我怎么就攤上這事食棕±屎停” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵簿晓,是天一觀的道長眶拉。 經(jīng)常有香客問我,道長憔儿,這世上最難降的妖魔是什么忆植? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谒臼,結果婚禮上朝刊,老公的妹妹穿的比我還像新娘。我一直安慰自己蜈缤,他們只是感情好拾氓,可當我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著底哥,像睡著了一般咙鞍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上趾徽,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天续滋,我揣著相機與錄音,去河邊找鬼孵奶。 笑死疲酌,一個胖子當著我的面吹牛,可吹牛的內容都是我干的了袁。 我是一名探鬼主播朗恳,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼早像!你這毒婦竟也來了僻肖?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤卢鹦,失蹤者是張志新(化名)和其女友劉穎臀脏,沒想到半個月后劝堪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡揉稚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年秒啦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搀玖。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡余境,死狀恐怖,靈堂內的尸體忽然破棺而出灌诅,到底是詐尸還是另有隱情芳来,我是刑警寧澤,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布猜拾,位于F島的核電站即舌,受9級特大地震影響,放射性物質發(fā)生泄漏挎袜。R本人自食惡果不足惜顽聂,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盯仪。 院中可真熱鬧紊搪,春花似錦、人聲如沸全景。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爸黄。三九已至娶牌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間馆纳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工汹桦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鲁驶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓舞骆,卻偏偏與公主長得像钥弯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子督禽,可洞房花燭夜當晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內容