微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)

一、點評

對於IM系統來講,如何作到IM聊天消息離線差別拉取(差別拉取是爲了節省流量)、消息多端同步、消息順序保證等,是典型的IM技術難點。php

就像即時通信網整理的如下IM開發乾貨系列同樣:html

IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞程序員

IM消息送達保證機制實現(二):保證離線消息的可靠投遞算法

如何保證IM實時消息的「時序性」與「一致性」?數據庫

IM單聊和羣聊中的在線狀態同步應該用「推」仍是「拉」?小程序

IM羣聊消息如此複雜,如何保證不丟不重?微信小程序

淺談移動端IM的多點登錄和消息漫遊原理數組

IM羣聊消息到底是存1份(即擴散讀)仍是存多份(即擴散寫)?緩存

上面這些文章所涉及的IM聊天消息的省流量、可靠投遞、離線拉取、時序性、一致性、多端同步等等問題,總結下來其實就是要解決好一個問題:即如何保證聊天消息的惟一性斷定和順序斷定。安全

不少羣友在討論這個問題的時候,廣泛考慮的是使用整型自增序列號做爲消息ID(即MsgId):這樣既能保證消息的惟一性又方便保證順序性,但問題是在分佈式狀況下是很難保證消息id的惟一性且順序遞增的,維護id生成的一致性難度太大了(網絡延遲、調試出錯等等均可能致使不一樣的機器取到的消息id存在碰撞的可能)。

 

不過,經過本文中微信團隊分享的微信消息序列號生成思路,實際上要解決消息的惟一性、順序性問題,能夠將一個技術點分解成兩個:即將原先每條消息一個自增且惟一的消息ID分拆成兩個關鍵屬性——消息ID(msgId)、消息序列號(seqId),即消息ID只要保證惟一性而不須要兼顧順序性(好比直接用UUID)、消息序列號只要保證順序性而不須要兼顧惟一性(就像本文中微信的思路同樣),這樣的技術分解就能很好的解決本來一個消息ID既要保證惟一性又要保證順序性的難題。

那麼,如何優雅地解決「消息序列號只要保證順序性而不須要兼顧惟一性」的問題呢?這就是本文所要分享的內容,強烈建議深刻理解和閱讀。

本文因篇幅較長,分爲上下兩篇,敬請點擊閱讀:

上篇:《微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)》(本文)

下篇:《微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)

學習交流:

- 即時通信開發交流3羣:185926912[推薦]

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

二、正文引言

微信在立項之初,就已確立了利用數據版本號(注:具體的實現也就是本文要分享的消息序列號)實現終端與後臺的數據增量同步機制,確保發消息時消息可靠送達對方手機,避免了大量潛在的家庭糾紛。時至今日,微信已經走過第五個年頭,這套同步機制仍然在消息收發、朋友圈通知、好友數據更新等須要數據同步的地方發揮着核心的做用。

而在這同步機制的背後,須要一個高可用、高可靠的消息序列號生成器來產生同步數據用的版本號(注:由於序列號天生的遞增特性,徹底能夠當版本號來使用,但又不只限於版本號的用途)。這個消息序列號生成器咱們微信內部稱之爲 seqsvr ,目前已經發展爲一個天天萬億級調用的重量級系統,其中每次申請序列號平時調用耗時1ms,99.9%的調用耗時小於3ms,服務部署於數百臺4核 CPU 服務器上。

本篇將重點介紹微信的消息序列號生成器 seqsvr 的算法原理、架構核心思想,以及 seqsvr 隨着業務量快速上漲所作的架構演變(下篇《微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)》會着重討論分佈式容災方案,敬請關注)。

 

三、關於做者

曾欽鬆:微信高級工程師,負責過微信基礎架構、微信翻譯引擎、微信圍棋PhoenixGo,致力於高可用高性能後臺系統的設計與研發。2011年畢業於西安電子科技大學,早先曾在騰訊搜搜從事檢索架構、分佈式數據庫方面的工做。

四、技術思路

微信服務器端爲每一份須要與客戶端同步的數據(例如聊天消息)都會賦予一個惟一的、遞增的序列號(後文稱爲 sequence ),做爲這份數據的版本號(這是利用了序列號遞增的特性)。在客戶端與服務器端同步的時候,客戶端會帶上已經同步下去數據的最大版本號,後臺會根據客戶端最大版本號與服務器端的最大版本號,計算出須要同步的增量數據,返回給客戶端。這樣不只保證了客戶端與服務器端的數據同步的可靠性,同時也大幅減小了同步時的冗餘數據(就像這篇文章中討論的同樣:《如何保證IM實時消息的「時序性」與「一致性」?》)。

這裏不用樂觀鎖機制來生成版本號,而是使用了一個獨立的 seqsvr 來處理序列號操做:

1)一方面由於業務有大量的 sequence 查詢需求——查詢已經分配出去的最後一個 sequence ,而基於 seqsvr 的查詢操做能夠作到很是輕量級,避免對存儲層的大量 IO 查詢操做;

2)另外一方面微信用戶的不一樣種類的數據存在不一樣的 Key-Value 系統中,使用統一的序列號有助於避免重複開發,同時業務邏輯能夠很方便地判斷一個用戶的各種數據是否有更新。

從 seqsvr 申請的、用做數據版本號的 sequence ,具備兩種基本的性質:

1)遞增的64位整型變量;

2)每一個用戶都有本身獨立的64位 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位數組,而咱們每個微信用戶,都在這個大數組裏獨佔一格8 bytes 的空間,這個格子就放着用戶已經分配出去的最後一個 sequence:cur_seq。每一個用戶來申請sequence的時候,只須要將用戶的cur_seq+=1,保存回數組,並返回給用戶。

 

▲ 圖1:小明申請了一個sequence,返回101

5.1 預分配中間層

任何一件看起來很簡單的事,在海量的訪問量下都會變得不簡單。前文提到,seqsvr 須要保證分配出去的sequence 遞增(數據可靠),還須要知足海量的訪問量(天天接近萬億級別的訪問)。知足數據可靠的話,咱們很容易想到把數據持久化到硬盤,可是按照目前每秒千萬級的訪問量(~10^7 QPS),基本沒有任何硬盤系統能扛住。

後臺架構設計不少時候是一門關於權衡的哲學,針對不一樣的場景去考慮能不能下降某方面的要求,以換取其它方面的提高。仔細考慮咱們的需求,咱們只要求遞增,並無要求連續,也就是說出現一大段跳躍是容許的(例如分配出的sequence序列:1,2,3,10,100,101)。

因而咱們實現了一個簡單優雅的策略:

1)內存中儲存最近一個分配出去的sequence:cur_seq,以及分配上限:max_seq;

2)分配sequence時,將cur_seq++,同時與分配上限max_seq比較:若是cur_seq > max_seq,將分配上限提高一個步長max_seq += step,並持久化max_seq;

3)重啓時,讀出持久化的max_seq,賦值給cur_seq。

 

▲ 圖2:小明、小紅、小白都各自申請了一個sequence,但只有小白的max_seq增長了步長100

這樣經過增長一個預分配 sequence 的中間層,在保證 sequence 不回退的前提下,大幅地提高了分配 sequence 的性能。實際應用中每次提高的步長爲10000,那麼持久化的硬盤IO次數從以前~10^7 QPS下降到~10^3 QPS,處於可接受範圍。在正常運做時分配出去的sequence是順序遞增的,只有在機器重啓後,第一次分配的 sequence 會產生一個比較大的跳躍,跳躍大小取決於步長大小。

5.2 分號段共享存儲

請求帶來的硬盤IO問題解決了,能夠支持服務平穩運行,但該模型仍是存在一個問題:重啓時要讀取大量的max_seq數據加載到內存中。

咱們能夠簡單計算下,以目前 uid(用戶惟一ID)上限2^32個、一個 max_seq 8bytes 的空間,數據大小一共爲32GB,從硬盤加載須要很多時間。另外一方面,出於數據可靠性的考慮,必然須要一個可靠存儲系統來保存max_seq數據,重啓時經過網絡從該可靠存儲系統加載數據。若是max_seq數據過大的話,會致使重啓時在數據傳輸花費大量時間,形成一段時間不可服務。

爲了解決這個問題,咱們引入號段 Section 的概念,uid 相鄰的一段用戶屬於一個號段,而同個號段內的用戶共享一個 max_seq,這樣大幅減小了max_seq 數據的大小,同時也下降了IO次數。

 

▲ 圖3:小明、小紅、小白屬於同個Section,他們共用一個max_seq。在每一個人都申請一個sequence的時候,只有小白突破了max_seq上限,須要更新max_seq並持久化

目前 seqsvr 一個 Section 包含10萬個 uid,max_seq 數據只有300+KB,爲咱們實現從可靠存儲系統讀取max_seq 數據重啓打下基礎。

5.3 工程實現

工程實如今上面兩個策略上作了一些調整,主要是出於數據可靠性及災難隔離考慮:

1)把存儲層和緩存中間層分紅兩個模塊 StoreSvr 及 AllocSvr 。StoreSvr 爲存儲層,利用了多機 NRW 策略來保證數據持久化後不丟失; AllocSvr 則是緩存中間層,部署於多臺機器,每臺 AllocSvr 負責若干號段的 sequence 分配,分攤海量的 sequence 申請請求。

2)整個系統又按 uid 範圍進行分 Set,每一個 Set 都是一個完整的、獨立的 StoreSvr+AllocSvr 子系統。分 Set 設計目的是爲了作災難隔離,一個 Set 出現故障只會影響該 Set 內的用戶,而不會影響到其它用戶。

 

▲ 圖4:原型架構圖

六、本篇小結

寫到這裏把 seqsvr 基本原型講完了,正是如此簡單優雅的模型,可靠、穩定地支撐着微信五年來的高速發展。五年裏訪問量一倍又一倍地上漲,seqsvr 自己也作過大大小小的重構,但 seqsvr 的分層架構一直沒有改變過,而且在可預見的將來裏也會一直保持不變。

原型跟生產環境的版本存在必定差距,最主要的差距在於容災上。像微信的 IM 類應用,對系統可用性很是敏感,而 seqsvr 又處於收發消息、朋友圈等功能的關鍵路徑上,對可用性要求很是高,出現長時間不可服務是分分鐘寫故障報告的節奏。

本文的下篇《微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)會講講 seqsvr 的容災方案演變。

附錄:更多QQ、微信團隊原創技術文章

微信朋友圈千億訪問量背後的技術挑戰和實踐總結

騰訊技術分享:騰訊是如何大幅下降帶寬和網絡流量的(圖片壓縮篇)

騰訊技術分享:騰訊是如何大幅下降帶寬和網絡流量的(音視頻技術篇)

微信團隊分享:微信移動端的全文檢索多音字問題解決方案

騰訊技術分享:Android版手機QQ的緩存監控與優化實踐

微信團隊分享:iOS版微信的高性能通用key-value組件技術實踐

微信團隊分享:iOS版微信是如何防止特殊字符致使的炸羣、APP崩潰的?

騰訊技術分享:Android手Q的線程死鎖監控系統技術實踐

微信團隊原創分享:iOS版微信的內存監控系統技術實踐

讓互聯網更快:新一代QUIC協議在騰訊的技術實踐分享

iOS後臺喚醒實戰:微信收款到帳語音提醒技術總結

騰訊技術分享:社交網絡圖片的帶寬壓縮技術演進之路

微信團隊分享:視頻圖像的超分辨率技術原理和應用場景

微信團隊分享:微信每日億次實時音視頻聊天背後的技術解密

QQ音樂團隊分享:Android中的圖片壓縮技術詳解(上篇)

QQ音樂團隊分享:Android中的圖片壓縮技術詳解(下篇)

騰訊團隊分享:手機QQ中的人臉識別酷炫動畫效果實現詳解

騰訊團隊分享 :一次手Q聊天界面中圖片顯示bug的追蹤過程分享

微信團隊分享:微信Android版小視頻編碼填過的那些坑》 

微信手機端的本地數據全文檢索優化之路》 

企業微信客戶端中組織架構數據的同步更新方案優化實戰

微信團隊披露:微信界面卡死超級bug「15。。。。」的前因後果

QQ 18年:解密8億月活的QQ後臺服務接口隔離技術

月活8.89億的超級IM微信是如何進行Android端兼容測試的

以手機QQ爲例探討移動端IM中的「輕應用」

一篇文章get微信開源移動端數據庫組件WCDB的一切!

微信客戶端團隊負責人技術訪談:如何着手客戶端性能監控和優化

微信後臺基於時間序的海量數據冷熱分級架構設計實踐

微信團隊原創分享:Android版微信的臃腫之困與模塊化實踐之路

微信後臺團隊:微信後臺異步消息隊列的優化升級實踐分享

微信團隊原創分享:微信客戶端SQLite數據庫損壞修復實踐》 

騰訊原創分享(一):如何大幅提高移動網絡下手機QQ的圖片傳輸速度和成功率》 

騰訊原創分享(二):如何大幅壓縮移動網絡下APP的流量消耗(下篇)》 

騰訊原創分享(三):如何大幅壓縮移動網絡下APP的流量消耗(上篇)》 

微信Mars:微信內部正在使用的網絡層封裝庫,即將開源》 

如約而至:微信自用的移動端IM網絡層跨平臺組件庫Mars已正式開源》 

開源libco庫:單機千萬鏈接、支撐微信8億用戶的後臺框架基石 [源碼下載]》 

微信新一代通訊安全解決方案:基於TLS1.3的MMTLS詳解》 

微信團隊原創分享:Android版微信後臺保活實戰分享(進程保活篇)》 

微信團隊原創分享:Android版微信後臺保活實戰分享(網絡保活篇)》 

Android版微信從300KB到30MB的技術演進(PPT講稿) [附件下載]》 

微信團隊原創分享:Android版微信從300KB到30MB的技術演進》 

微信技術總監談架構:微信之道——大道至簡(演講全文)

微信技術總監談架構:微信之道——大道至簡(PPT講稿) [附件下載]》 

如何解讀《微信技術總監談架構:微信之道——大道至簡》

微信海量用戶背後的後臺系統存儲架構(視頻+PPT) [附件下載]

微信異步化改造實踐:8億月活、單機千萬鏈接背後的後臺解決方案》 

微信朋友圈海量技術之道PPT [附件下載]》 

微信對網絡影響的技術試驗及分析(論文全文)》 

一份微信後臺技術架構的總結性筆記》 

架構之道:3個程序員成就微信朋友圈日均10億發佈量[有視頻]》 

快速裂變:見證微信強大後臺架構從0到1的演進歷程(一)

快速裂變:見證微信強大後臺架構從0到1的演進歷程(二)》 

微信團隊原創分享:Android內存泄漏監控和優化技巧總結》 

全面總結iOS版微信升級iOS9遇到的各類「坑」》 

微信團隊原創資源混淆工具:讓你的APK立減1M》 

微信團隊原創Android資源混淆工具:AndResGuard [有源碼]》 

Android版微信安裝包「減肥」實戰記錄》 

iOS版微信安裝包「減肥」實戰記錄》 

移動端IM實踐:iOS版微信界面卡頓監測方案》 

微信「紅包照片」背後的技術難題》 

移動端IM實踐:iOS版微信小視頻功能技術方案實錄》 

移動端IM實踐:Android版微信如何大幅提高交互性能(一)

移動端IM實踐:Android版微信如何大幅提高交互性能(二)

移動端IM實踐:實現Android版微信的智能心跳機制》 

移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》 

移動端IM實踐:谷歌消息推送服務(GCM)研究(來自微信)

移動端IM實踐:iOS版微信的多設備字體適配方案探討》 

信鴿團隊原創:一塊兒走過 iOS10 上消息推送(APNS)的坑

騰訊信鴿技術分享:百億級實時消息推送的實戰經驗

IPv6技術詳解:基本概念、應用現狀、技術實踐(上篇)

IPv6技術詳解:基本概念、應用現狀、技術實踐(下篇)

騰訊TEG團隊原創:基於MySQL的分佈式數據庫TDSQL十年鍛造經驗分享

微信多媒體團隊訪談:音視頻開發的學習、微信的音視頻技術和挑戰等

瞭解iOS消息推送一文就夠:史上最全iOS Push技術詳解

騰訊技術分享:微信小程序音視頻技術背後的故事

騰訊資深架構師乾貨總結:一文讀懂大型分佈式系統設計的方方面面

微信多媒體團隊梁俊斌訪談:聊一聊我所瞭解的音視頻技術

騰訊音視頻實驗室:使用AI黑科技實現超低碼率的高清實時視頻聊天

騰訊技術分享:微信小程序音視頻與WebRTC互通的技術思路和實踐

手把手教你讀取Android版微信和手Q的聊天記錄(僅做技術研究學習)

微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)

>> 更多同類文章 ……

(本文同步發佈於:http://www.52im.net/thread-1998-1-1.html

相關文章
相關標籤/搜索