2011.1.21 微信正式發佈。這一天距離微信項目啓動日約爲2個月。就在這2個月裏,微信從無到有,你們可能會好奇這期間微信後臺作的最重要的事情是什麼?web
我想應該是如下三件事:算法
微信起初定位是一個通信工具,做爲通信工具最核心的功能是收發消息。微信團隊源於廣硏團隊,消息模型跟郵箱的郵件模型也頗有淵源,都是存儲轉發。數據庫
圖 1 微信消息模型安全
圖1展現了這一消息模型,消息被髮出後,會先在後臺臨時存儲;爲使接收者能更快接收到消息,會推送消息通知給接收者;最後客戶端主動到服務器收取消息。性能優化
因爲用戶的賬戶、聯繫人和消息等數據都在服務器存儲,如何將數據同步到客戶端就成了很關鍵的問題。爲簡化協議,咱們決定經過一個統一的數據同步協議來同步用戶全部的基礎數據。服務器
最初的方案是客戶端記錄一個本地數據的快照(Snapshot),須要同步數據時,將Snapshot帶到服務器,服務器經過計算Snapshot與服務器數據的差別,將差別數據發給客戶端,客戶端再保存差別數據完成同步。不過這個方案有兩個問題:一是Snapshot會隨着客戶端數據的增多變得愈來愈大,同步時流量開銷大;二是客戶端每次同步都要計算Snapshot,會帶來額外的性能開銷和實現複雜度。微信
幾經討論後,方案改成由服務計算Snapshot,在客戶端同步數據時跟隨數據一塊兒下發給客戶端,客戶端無需理解Snapshot,只需存儲起來,在下次數據同步數據時帶上便可。同時,Snapshot被設計得很是精簡,是若干個Key-Value的組合,Key表明數據的類型,Value表明給到客戶端的數據的最新版本號。Key有三個,分別表明:賬戶數據、聯繫人和消息。這個同步協議的一個額外好處是客戶端同步完數據後,不須要額外的ACK協議來確認數據收取成功,一樣能夠保證不會丟數據:只要客戶端拿最新的Snapshot到服務器作數據同步,服務器便可確認上次數據已經成功同步完成,能夠執行後續操做,例如清除暫存在服務的消息等等。網絡
此後,精簡方案、減小流量開銷、儘可能由服務器完成較複雜的業務邏輯、下降客戶端實現的複雜度就做爲重要的指導原則,持續影響着後續的微信設計開發。記得有個比較經典的案例是:咱們在微信1.2版實現了羣聊功能,但爲了保證新舊版客戶端間的羣聊體驗,咱們經過服務器適配,讓1.0版客戶端也能參與羣聊。架構
圖 2 微信後臺系統架構併發
微信後臺使用三層架構:接入層、邏輯層和存儲層。
接入層提供接入服務,包括長鏈接入服務和短鏈接入服務。長鏈接入服務同時支持客戶端主動發起請求和服務器主動發起推送;短鏈接入服務則只支持客戶端主動發起請求。
邏輯層包括業務邏輯服務和基礎邏輯服務。業務邏輯服務封裝了業務邏輯,是後臺提供給微信客戶端調用的API。基礎邏輯服務則抽象了更底層和通用的業務邏輯,提供給業務邏輯服務訪問。
存儲層包括數據訪問服務和數據存儲服務。數據存儲服務經過MySQL和SDB(廣硏早期後臺中普遍使用的Key-Table數據存儲系統)等底層存儲系統來持久化用戶數據。數據訪問服務適配並路由數據訪問請求到不一樣的底層數據存儲服務,面向邏輯層提供結構化的數據服務。比較特別的是,微信後臺每一種不一樣類型的數據都使用單獨的數據訪問服務和數據存儲服務,例如賬戶、消息和聯繫人等等都是獨立的。
微信後臺主要使用C++。後臺服務使用Svrkit框架搭建,服務之間經過同步RPC進行通信。
圖 3 Svrkit 框架
Svrkit是另外一個廣硏後臺就已經存在的高性能RPC框架,當時還沒有普遍使用,但在微信後臺卻大放異彩。做爲微信後臺基礎設施中最重要的一部分,Svrkit這幾年一直不斷在進化。咱們使用Svrkit構建了數以千計的服務模塊,提供數萬個服務接口,天天RPC調用次數達幾十萬億次。
這三件事影響深遠,乃至於5年後的今天,咱們仍繼續沿用最初的架構和協議,甚至還能夠支持當初1.0版的微信客戶端。
這裏有一個經驗教訓——運營支撐系統真的很重要。第一個版本的微信後臺是倉促完成的,當時只是完成了基礎業務功能,並無配套的業務數據統計等等。咱們在開放註冊後,一時間竟沒有業務監控頁面和數據曲線能夠看,註冊用戶數是臨時從數據庫統計的,在線數是從日誌裏提取出來的,這些數據經過每一個小時運行一次的腳本(這個腳本也是當天臨時加的)統計出來,而後自動發郵件到郵件組。還有其餘各類業務數據也經過郵件進行發佈,能夠說郵件是微信初期最重要的數據門戶。
2011.1.21 當天最高併發在線數是 491,而今天這個數字是4億。
在微信發佈後的4個多月裏,咱們經歷了發佈後火爆註冊的驚喜,也經歷了隨後一直不溫不火的困惑。
這一時期,微信作了不少旨在增長用戶好友量,讓用戶聊得起來的功能。打通騰訊微博私信、羣聊、工做郵箱、QQ/郵箱好友推薦等等。對於後臺而言,比較重要的變化就是這些功能催生了對異步隊列的需求。例如,微博私信須要跟外部門對接,不一樣系統間的處理耗時和速度不同,能夠經過隊列進行緩衝;羣聊是耗時操做,消息發到羣后,能夠經過異步隊列來異步完成消息的擴散寫等等。
圖 4 單聊和羣聊消息發送過程
圖4是異步隊列在羣聊中的應用。微信的羣聊是寫擴散的,也就是說發到羣裏的一條消息會給羣裏的每一個人都存一份(消息索引)。
爲何不是讀擴散呢?有兩個緣由:
羣的人數很少,羣人數上限是10(後來逐步加到20、40、100,目前是500),擴散的成本不是太大,不像微博,有成千上萬的粉絲,發一條微博後,每粉絲都存一份的話,一個是效率過低,另外一個存儲量也會大不少;
消息擴散寫到每一個人的消息存儲(消息收件箱)後,接收者到後臺同步數據時,只須要檢查本身收件箱便可,同步邏輯跟單聊消息是一致的,這樣能夠統一數據同步流程,實現起來也會很輕量。
異步隊列做爲後臺數據交互的一種重要模式,成爲了同步RPC服務調用以外的有力補充,在微信後臺被大量使用。
微信的飛速發展是從2.0版開始的,這個版本發佈了語音聊天功能。以後微信用戶量急速增加,2011.5用戶量破100萬、2011.7 用戶量破1000萬、2012.3 註冊用戶數突破1億。伴隨着喜人成績而來的,還有一堆幸福的煩惱。
業務快速迭代的壓力
微信發佈時功能很簡單,主要功能就是發消息。不過在發語音以後的幾個版本里迅速推出了手機通信錄、QQ離線消息、查看附近的人、搖一搖、漂流瓶和朋友圈等等功能。有個廣爲流傳的關於朋友圈開發的傳奇——朋友圈歷經4個月,先後作了30多個版本迭代才最終成型。其實還有一個不爲人知的故事——那時候由於人員比較短缺,朋友圈後臺長時間只有1位開發人員。
後臺穩定性的要求
用戶多了,功能也多了,後臺模塊數和機器量在不斷翻番,緊跟着的還有各類故障。
幫助咱們順利度過這個階段的,是如下幾個舉措:
雖然各類需求撲面而來,但咱們每一個實現方案都是一絲不苟完成的。實現需求最大的困難不是設計出一個方案並實現出來,而是須要在若干個可能的方案中,甄選出最簡單實用的那個。
這中間每每須要通過幾輪思考——討論——推翻的迭代過程,謀定然後動有很多好處,一方面能夠避免作出華而不實的過分設計,提高效率;另外一方面,經過詳盡的討論出來的看似簡單的方案,細節考究,每每是可靠性最好的方案。
邏輯層的業務邏輯服務最先只有一個服務模塊(咱們稱之爲mmweb),囊括了全部提供給客戶端訪問的API,甚至還有一個完整的微信官網。這個模塊架構相似Apache,由一個CGI容器(CGIHost)和若干CGI組成(每一個CGI即爲一個API),不一樣之處在於每一個CGI都是一個動態庫so,由CGIHost動態加載。
在mmweb的CGI數量相對較少的時候,這個模塊的架構徹底能知足要求,但當功能迭代加快,CGI量不斷增多以後,開始出現問題:
1) 每一個CGI都是動態庫,在某些CGI的共用邏輯的接口定義發生變化時,不一樣時期更新上線的CGI可能使用了不一樣版本的邏輯接口定義,會致使在運行時出現詭異結果或者進程crash,並且很是難以定位;
2) 全部CGI放在一塊兒,每次大版本發佈上線,從測試到灰度再到全面部署完畢,都是一個很漫長的過程,幾乎全部後臺開發人員都會被同時卡在這個環節,很是影響效率;
3) 新增的不過重要的CGI有時穩定性很差,某些異常分支下會crash,致使CGIHost進程沒法服務,發消息這些重要CGI受影響無法運行。
因而咱們開始嘗試使用一種新的CGI架構——Logicsvr。
Logicsvr基於Svrkit框架。將Svrkit框架和CGI邏輯經過靜態編譯生成可直接使用HTTP訪問的Logicsvr。咱們將mmweb模塊拆分爲8個不一樣服務模塊。拆分原則是:實現不一樣業務功能的CGI被拆到不一樣Logicsvr,同一功能可是重要程度不同的也進行拆分。例如,做爲核心功能的消息收發邏輯,就被拆爲3個服務模塊:消息同步、發文本和語音消息、發圖片和視頻消息。
每一個Logicsvr都是一個獨立的二進制程序,能夠分開部署、獨立上線。時至今日,微信後臺有數十個Logicsvr,提供了數百個CGI服務,部署在數千臺服務器上,每日客戶端訪問量幾千億次。
除了API服務外,其餘後臺服務模塊也遵循「大系統小作」這一實踐準則,微信後臺服務模塊數從微信發佈時的約10個模塊,迅速上漲到數百個模塊。
這一時期,後臺故障不少。比故障更麻煩的是,由於監控的缺失,常常有些故障咱們無法第一時間發現,形成故障影響面被放大。
監控的缺失一方面是由於在快速迭代過程當中,重視功能開發,輕視了業務監控的重要性,有故障一直是兵來將擋水來土掩;另外一方面是基礎設施對業務邏輯監控的支持度較弱。基礎設施提供了機器資源監控和Svrkit服務運行狀態的監控。這個是每臺機器、每一個服務標配的,無需額外開發,可是業務邏輯的監控就要麻煩得多了。當時的業務邏輯監控是經過業務邏輯統計功能來作的,實現一個監控須要4步:
1) 申請日誌上報資源;
2) 在業務邏輯中加入日誌上報點,日誌會被每臺機器上的agent收集並上傳到統計中心;
3) 開發統計代碼;
4) 實現統計監控頁面。
能夠想象,這種費時費力的模式會反過來下降開發人員對加入業務監控的積極性。因而有一天,咱們去公司內的標杆——即通後臺(QQ後臺)取經了,發現解決方案出乎意料地簡單且強大:
3.1) 故障報告
以前每次故障後,是由QA牽頭出一份故障報告,着重點是對故障影響的評估和故障定級。新的作法是每一個故障不分大小,開發人員須要完全覆盤故障過程,而後商定解決方案,補充出一份詳細的技術報告。這份報告側重於:如何避免同類型故障再次發生、提升故障主動發現能力、縮短故障響應和處理過程。
3.2) 基於 ID-Value 的業務無關的監控告警體系
圖 5 基於 ID-Value 的監控告警體系
監控體系實現思路很是簡單,提供了2個API,容許業務代碼在共享內存中對某個監控ID進行設置Value或累加Value的功能。每臺機器上的Agent會定時將全部ID-Value上報到監控中心,監控中心對數據彙總入庫後就能夠經過統一的監控頁面輸出監控曲線,並經過預先配置的監控規則產生報警。
對於業務代碼來講,只需在要被監控的業務流程中調用一下監控API,並配置好告警條件便可。這就極大地下降了開發監控報警的成本,咱們補全了各類監控項,讓咱們能主動及時地發現問題。新開發的功能也會預先加入相關監控項,以便在少許灰度階段就能直接經過監控曲線瞭解業務是否符合預期。
微信後臺每一個存儲服務都有本身獨立的存儲模塊,是相互獨立的。每一個存儲服務都有一個業務訪問模塊和一個底層存儲模塊組成。業務訪問層隔離業務邏輯層和底層存儲,提供基於RPC的數據訪問接口;底層存儲有兩類:SDB和MySQL。
SDB適用於以用戶UIN(uint32_t)爲Key的數據存儲,比方說消息索引和聯繫人。優勢是性能高,在可靠性上,提供基於異步流水同步的Master-Slave模式,Master故障時,Slave能夠提供讀數據服務,沒法寫入新數據。
因爲微信帳號爲字母+數字組合,沒法直接做爲SDB的Key,因此微信賬號數據並不是使用SDB,而是用MySQL存儲的。MySQL也使用基於異步流水複製的Master-Slave模式。
第1版的賬號存儲服務使用Master-Slave各1臺。Master提供讀寫功能,Slave不提供服務,僅用於備份。當Master有故障時,人工切讀服務到Slave,沒法提供寫服務。爲提高訪問效率,咱們還在業務訪問模塊中加入了memcached提供Cache服務,減小對底層存儲訪問。
第2版的賬號存儲服務仍是Master-Slave各1臺,區別是Slave能夠提供讀服務,但有可能讀到髒數據,所以對一致性要求高的業務邏輯,例如註冊和登陸邏輯只容許訪問Master。當Master有故障時,一樣只能提供讀服務,沒法提供寫服務。
第3版的賬號存儲服務採用1個Master和多個Slave,解決了讀服務的水平擴展能力。
第4版的賬號服務底層存儲採用多個Master-Slave組,每組由1個Master和多個Slave組成,解決了寫服務能力不足時的水平擴展能力。
最後還有個未解決的問題:單個Master-Slave分組中,Master仍是單點,沒法提供實時的寫容災,也就意味着沒法消除單點故障。另外Master-Slave的流水同步延時對讀服務有很大影響,流水出現較大延時會致使業務故障。因而咱們尋求一個能夠提供高性能、具有讀寫水平擴展、沒有單點故障、可同時具有讀寫容災能力、能提供強一致性保證的底層存儲解決方案,最終KVSvr應運而生。
KVSvr使用基於Quorum的分佈式數據強一致性算法,提供Key-Value/Key-Table模型的存儲服務。傳統Quorum算法的性能不高,KVSvr創造性地將數據的版本和數據自己作了區分,將Quorum算法應用到數據的版本的協商,再經過基於流水同步的異步數據複製提供了數據強一致性保證和極高的數據寫入性能,另外KVSvr自然具有數據的Cache能力,能夠提供高效的讀取性能。
KVSvr一舉解決了咱們當時迫切須要的無單點故障的容災能力。除了第5版的賬號服務外,很快全部SDB底層存儲模塊和大部分MySQL底層存儲模塊都切換到KVSvr。隨着業務的發展,KVSvr也不斷在進化着,還配合業務須要衍生出了各類定製版本。如今的KVSvr仍然做爲核心存儲,發揮着舉足輕重的做用。
2011.8 深圳舉行大運會。微信推出「微信深圳大運志願者服務中心」服務號,微信用戶能夠搜索「szdy」將這個服務號加爲好友,獲取大會相關的資訊。當時後臺對「szdy」作了特殊處理,用戶搜索時,會隨機返回「szdy01」,「szdy02」,…,「szdy10」這10個微信號中的1個,每一個微信號背後都有一個志願者在服務。
2011.9 「微成都」落戶微信平臺,微信用戶能夠搜索「wechengdu」加好友,成都市民還能夠在「附近的人」看到這個號,咱們在後臺給這個賬號作了一些特殊邏輯,能夠支持後臺自動回覆用戶發的消息。
這種需求愈來愈多,咱們就開始作一個媒體平臺,這個平臺後來從微信後臺分出,演變成了微信公衆平臺,獨立發展壯大,開始了微信的平臺化之路。除微信公衆平臺外,微信後臺的外圍還陸續出現了微信支付平臺、硬件平臺等等一系列平臺。
圖 6 微信平臺
微信走出國門的嘗試開始於3.0版本。從這個版本開始,微信逐步支持繁體、英文等多種語言文字。不過,真正標誌性的事情是第一個海外數據中心的投入使用。
海外數據中心的定位是一個自治的系統,也就是說具有完整的功能,可以不依賴於國內數據中心獨立運做。
1) 多數據中心架構
圖 7 多數據中心架構
系統自治對於無狀態的接入層和邏輯層來講很簡單,全部服務模塊在海外數據中心部署一套就好了。
可是存儲層就有很大麻煩了——咱們須要確保國內數據中心和海外數據中心能獨立運做,但不是兩套隔離的系統各自部署,各玩各的,而是一套業務功能能夠徹底互通的系統。所以咱們的任務是須要保證兩個數據中心的數據一致性,另外Master-Master架構是個必選項,也即兩個數據中心都須要可寫。
2) Master-Master 存儲架構
Master-Master架構下數據的一致性是個很大的問題。兩個數據中心之間是個高延時的網絡,意味着在數據中心之間直接使用Paxos算法、或直接部署基於Quorum的KVSvr等看似一勞永逸的方案不適用。
最終咱們選擇了跟Yahoo!的PNUTS系統相似的解決方案,須要對用戶集合進行切分,國內用戶以國內上海數據中心爲Master,全部數據寫操做必須回到國內數據中心完成;海外用戶以海外數據中心爲Master,寫操做只能在海外數據中心進行。從總體存儲上看,這是一個Master-Master的架構,但細到一個具體用戶的數據,則是Master-Slave模式,每條數據只能在用戶歸屬的數據中心可寫,再異步複製到其餘數據中心。
圖 8 多數據中心的數據Master-Master架構
3) 數據中心間的數據一致性
這個Master-Master架構能夠在不一樣數據中心間實現數據最終一致性。如何保證業務邏輯在這種數據弱一致性保證下不會出現問題?
這個問題能夠被分解爲2個子問題:
用戶訪問本身的數據
用戶能夠滿世界跑,那是否容許用戶就近接入數據中心就對業務處理流程有很大影響。若是容許就近接入,同時還要保證數據一致性不影響業務,就意味着要麼用戶數據的Master須要能夠動態的改變;要麼須要對全部業務邏輯進行仔細梳理,嚴格區分本數據中心和跨數據中心用戶的請求,將請求路由到正確的數據中心處理。
考慮到上述問題會帶來很高昂的實現和維護的複雜度,咱們限制了每一個用戶只能接入其歸屬數據中心進行操做。若是用戶發生漫遊,其漫遊到的數據中心會自動引導用戶從新連回歸屬數據中心。
這樣用戶訪問本身數據的一致性問題就迎刃而解了,由於全部操做被限制在歸屬數據中心內,其數據是有強一致性保證的。此外,還有額外的好處:用戶本身的數據(如:消息和聯繫人等)不須要在數據中心間同步,這就大大下降了對數據同步的帶寬需求。
用戶訪問其餘用戶的數據
因爲不一樣數據中心之間業務須要互通,用戶會使用到其餘數據中心用戶建立的數據。例如,參與其餘數據中心用戶建立的羣聊,查看其餘數據中心用戶的朋友圈等。
仔細分析後能夠發現,大部分場景下對數據一致性要求其實並不高。用戶稍遲些才見到本身被加入某個其餘數據中心用戶建的羣、稍遲些才見到某個好友的朋友圈動態更新其實並不會帶來什麼問題。在這些場景下,業務邏輯直接訪問本數據中心的數據。
固然,仍是有些場景對數據一致性要求很高。比方說給本身設置微信號,而微信號是須要在整個微信賬號體系裏保證惟一的。咱們提供了全局惟一的微信號申請服務來解決這一問題,全部數據中心經過這個服務申請微信號。這種須要特殊處置的場景極少,不會帶來太大問題。
4) 可靠的數據同步
數據中心之間有大量的數據同步,數據是否可以達到最終一致,取決於數據同步是否可靠。爲保證數據同步的可靠性,提高同步的可用性,咱們又開發一個基於Quorum算法的隊列組件,這個組件的每一組由3機存儲服務組成。與通常隊列的不一樣之處在於,這個組件對隊列寫入操做進行了大幅簡化,3機存儲服務不須要相互通信,每一個機器上的數據都是順序寫,執行寫操做時在3機能寫入成功2份即爲寫入成功;若失敗,則換另一組再試。所以這個隊列能夠達到極高的可用性和寫入性能。每一個數據中心將須要同步的數據寫入本數據中心的同步隊列後,由其餘數據中心的數據重放服務將數據拉走並進行重放,達到數據同步的目的。
海外數據中心建設週期長,投入大,微信只在香港和加拿大有兩個海外數據中心。但世界那麼大,即使是這兩個數據中心,也仍是無法輻射全球,讓各個角落的用戶都能享受到暢快的服務體驗。
經過在海外實際對比測試發現,微信客戶端在發消息等一些主要使用場景與主要競品有不小的差距。爲此,咱們跟公司的架構平臺部、網絡平臺部和國際業務部等兄弟部門一塊兒合做,圍繞海外數據中心,在世界各地精心選址建設了數十個POP點(包括信令加速點和圖片CDN網絡)。另外,經過對移動網絡的深刻分析和研究,咱們還對微信的通信協議作了大幅優化。微信最終在對比測試中遇上並超過了主要的競品。
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) 容災效果檢驗
三園區容災是否能正常發揮做用還須要進行實際的檢驗,咱們在上海數據中心和海外的香港數據中心完成三園區建設後,進行了數次實戰演習,屏蔽單一園區上千臺服務,檢驗容災效果是否符合預期。特別地,爲了不隨着時間的推移某個核心服務模塊由於某次更新就再也不支持三園區容災了,咱們還搭建了一套容災撥測系統,天天對全部服務模塊選取某個園區的服務主動屏蔽掉,自動檢查服務總體失敗量是否發生變化,實現對三園區容災效果的持續檢驗。
以前咱們在業務迅速發展之時,優先支撐業務功能快速迭代,性能問題無暇兼顧,比較粗放的貫徹了「先扛住再優化」的海量之道。2014年開始大幅縮減運營成本,性能優化就被提上了日程。
咱們基本上對大部分服務模塊的設計和實現都進行了從新review,並進行了有針對性的優化,這仍是能夠節約出很多機器資源的。但更有效的優化措施是對基礎設施的優化,具體的說是對Svrkit框架的優化。Svrkit框架被普遍應用到幾乎全部服務模塊,若是框架層面能把機器資源使用到極致,那確定是事半功倍的。
結果還真的能夠,咱們在基礎設施里加入了對協程的支持,重點是這個協程組件能夠不破壞原來的業務邏輯代碼結構,讓咱們原有代碼中使用同步RPC調用的代碼不作任何修改,就能夠直接經過協程異步化。Svrkit框架直接集成了這個協程組件,而後美好的事情發生了,原來單實例最多提供上百併發請求處理能力的服務,在重編上線後,轉眼間就能提供上千併發請求處理能力。Svrkit框架的底層實如今這一時期也作了全新的實現,服務的處理能力大幅提升。
咱們一直以來都不太擔憂某個服務實例出現故障,致使這個實例徹底沒法提供服務的問題,這個在後臺服務的容災體系裏能夠被處理得很好。最擔憂的是雪崩:某個服務由於某些緣由出現過載,致使請求處理時間被大大拉長。因而服務吞吐量降低,大量請求積壓在服務的請求隊列太長時間了,致使訪問這個服務的上游服務出現超時。更倒黴的是上游服務還常常會重試,而後這個過載的服務僅有的一點處理能力都在作無用功(即處理完畢返回結果時,調用端都已超時放棄),終於這個過載的服務完全雪崩了。最糟糕的狀況是上游服務每一個請求都耗時那麼久,雪崩順着RPC調用鏈一級級往上傳播,最終單個服務模塊的過載會引起大批服務模塊的雪崩。
咱們在一番勒緊褲腰帶節省機器資源、消滅低負載機器後,全部機器的負載都上來了,服務過載變得常常發生了。解決這一問題的有力武器是Svrkit框架裏的具備QoS保障的FastReject機制,能夠快速拒絕掉超過服務自身處理能力的請求,即便在過載時,也能穩定地提供有效輸出。
近年,互聯網安全事件時有發生,各類拖庫層出不窮。爲保護用戶的隱私數據,咱們建設了一套數據保護系統——全程票據系統。其核心方案是,用戶登陸後,後臺會下發一個票據給客戶端,客戶端每次請求帶上票據,請求在後臺服務的整個處理鏈條中,全部對核心數據服務的訪問,都會被校驗票據是否合法,非法請求會被拒絕,從而保障用戶隱私數據只能用戶經過本身的客戶端發起操做來訪問。
微信後臺有成千的服務模塊,部署在全球數以萬計的服務器上,一直依靠人工管理。此外,微信後臺主要是提供實時在線服務,天天的服務器資源佔用在業務高峯和低谷時相差很大,在業務低谷時計算資源被白白浪費;另外一方面,不少離線的大數據計算卻受制於計算資源不足,難以高效完成。
咱們正在實驗和部署的資源調度系統(Yard)能夠把機器資源的分配和服務的部署自動化、把離線任務的調度自動化,實現了資源的優化配置,在業務對服務資源的需求有變化時,能更及時、更彈性地自動實現服務的從新配置與部署。
基於Quorum算法的KVSvr已經實現了強一致性、高可用且高性能的Key-Value/Key-Table存儲。最近,微信後臺又誕生了基於Paxos算法的另外一套存儲系統,首先落地的是PhxSQL,一個支持完整MySQL功能,又同時具有強一致性、高可用和高性能的SQL存儲。