1、引言
經歷過稍有些規模的IM系統開發的同行們都有體會,要想實現大規模并發IM(比如億級用戶和數十億日消息量這樣的規模),在架構設計上需要一些額外的考慮,尤其是要解決用戶高并發、服務高可用,架構和實現細節上都需要不短時間的打磨。
我在過往的工作經歷里,親手設計和實現了一套億級用戶量的IM,平臺上線并經過6年多的驗證,穩定性和可用性被驗證完全達到預期。
這套IM系統,從上線至今已6年有余,本人也已經離職創業近2年,但當初設計和開發這套系統時積累和收獲了大量的第一手實踐經驗和技術心得。
因此,想借本文把當時的架構設計經歷記錄下來,作為同行交流和參考,希望能提供一些啟發,少走彎路。

本文已同步發布于“即時通訊技術圈”公眾號,歡迎關注。公眾號上的鏈接是:點此進入。
2、系列文章
為了更好以進行內容呈現,本文拆分兩了上下兩篇。
本文是2篇文章中的第1篇:
《一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等》(本文)
《一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等(稍后發布...)》
本篇主要總結和分享這套IM架構的總體設計和服務拆分等。
3、原作者
本文基于鄧昀澤的“大規模并發IM服務架構設計”一文進行的擴展和修訂,感謝原作者的分享。

鄧昀澤:畢業于北京航空航天大學,現藍貓微會創始人兼CEO,曾就職于美團、YY語音、微軟和金山軟件等公司,有十多年研發管理經驗。
4、技術指標
在這套IM系統的架構上,技術上我們堅持高要求,經過數年的驗證,也確實達到了設計預期。
這4大技術指標是:

具體解釋就是:
- 1)高可靠:確保不丟消息;
- 2)高可用:任意機房或者服務器掛掉,不影響服務;
- 3)實時性:不管用戶在哪里,在線用戶消息在1秒內達到(我們實際是75%消息可以做到120ms);
- 4)有序性:確保用戶消息的有序性,不會出現發送和接受的亂序。
5、架構拆分
從整體架構上來說,億級用戶量的IM架構整體上偏復雜。
傳統開源的IM服務喜歡把所有服務做到1-2個服務里(Connector+Service模型),這樣帶來的問題比較嚴重。
傳統開源的IM的問題主要體現在:
- 1)服務代碼復雜,難以持續開發和運維;
- 2)單一業務邏輯出問題,可能會影響到其它邏輯,導致服務的全面不可用。
因此,我在做架構設計的時候盡量追求微服務化。即把整體架構進行分拆為子系統,然后子系統內按照業務邏輯分拆為微服務。
系統拆分如下圖:

4個子系統的職責是:
- 1)IM業務系統:服務IM相關的業務邏輯(比如好友關系、群關系、用戶信息等);
- 2)信令系統:負責用戶登錄,用戶在線狀態的維護,以及在線用戶的下行推送;
- 3)推送系統:負責消息的在線推送和離線推送;
- 4)存儲系統:負責消息和文件的存儲和查詢;
其中:信令系統和推送系統是基礎設施,不只是可以為IM業務服務,也可以承載其它類似的業務邏輯(比如客服系統)。
在部署層面:采用存儲3核心機房,信令和推送節點按需部署的方式(國內業務推薦8-10個點)。實際上我們只做了了北京3個機房,上海1個機房和香港一個機房的部署,就基本上滿足了大陸+香港的業務需求。
下面將逐個介紹這4個子系統的細節方面。
6、IM業務系統
一說到IM,很多人腦海里跳出的第一個關鍵就是“即時通信”,技術上理所當然的聯想到了socket,也就是大家成天嘴上說的:“長連接”。換句話說,很多對IM不了解或了解的不多的人,認為IM里的所有數據交互、業務往來都是通過“長連接”來實現的,這樣話,對于本文章中拆分出的“IM業務系統”就有點不理解了。
實際上,早期的IM(比如20年前的QQ、MSN、ICQ),確實所有數據基本都是通過“長連接”(也就是程序員所說的“socket”)實現。
但如今,移動端為主端的IM時代,IM系統再也不是一個條“長連接”走天下。
現在,一個典型的IM系統數據往來通常拆分成兩種服務:
- 1)socket長連接服務(也就是本文中的“推送服務”);
- 2)http短連接服務(就是最常用的http rest接口那些,也就是本文中的“IM業務系統”)。
通俗一點,也也就現在的IM系統,通常都是長、短連接配合一起實現的。

比如論壇里很多熱門技術方案都是這樣來做的,比如最典型的這兩篇:《IM單聊和群聊中的在線狀態同步應該用“推”還是“拉”?》、《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》,文記里提到的“推”其實就是走的“長連接”、“拉”就上指的http短連接。
對于socket長連接服務就沒什么好說,就是大家最常理解的那樣。
IM業務系統詳細來說,就是專注處理IM相關的業務邏輯,比如:
- 1)維護用戶數據:用戶基本信息等;
- 2)維護好友關系:好友請求、好友列表、好友信息等;
- 3)維護群組信息:群創建、解散、成員管理等;
- 4)提供數據:離線拉取、歷史記錄同步;
- 5)其它邏輯:比如通過存儲和推送系統,存儲消息和發送通知;
按照微服務的原則,IM業務系統也被分拆為多個服務,比如:
- 1)GInfo服務:群組信息維護;
- 2)IM服務:處理1V1消息;
- 3)GIM服務:處理群組消息。
7、信令系統
7.1 基本情況
信令系統主要職責是3部分:

1)維護用戶在線狀態:
因為用戶規模龐大,必然是多個集群,每個集群多臺服務器為用戶提供服務。
考慮到服務器運維的復雜性,我們要假定任何一個集群,任何一個服務器都可能會掛掉,而且在這種情況下要能夠繼續為用戶提供服務。
在這種情況下,如果用戶A給用戶B發消息,我們需要知道用戶B在哪個服務器上,才能把消息正確推送給用戶B。用戶在哪個信令服務,這個信息就是在線狀態數據。
2)下行消息推送:
跟上一個職責有關,用戶在線的時候,如果有其它用戶給他發消息,那就最好不要走離線推送,而是走在線推送。
在線推送的最后一個環節,是把用戶消息推送給用戶設備,因為就需要知道用戶登錄到哪個服務器上。
3)業務分發:
信令服務不只可以處理IM請求,也可以處理其它類型的業務請求。為了處理不同的業務,就需要有分發能力。
具體做法是通過一個SVID(service id)來實現,不同的業務攜帶不同的SVID,信令服務就知道如何分發了。
用戶通過登錄服務把數據(比如IM消息)發送到信令系統,信令系統根據SVID轉發給IM系統。不管后臺有多少個業務,用戶只需要一條鏈接到信令。
7.2 服務拆分
信令系統為了實現以上這3個職責,同時要確保我們服務可平行擴展的能力和穩定性,在實際的技術實現上,我們實際上把信令服務分拆為3個服務模塊。
如下圖所示:

下面將逐個介紹這3個子服務。
7.3 Login服務
Login服務主要負責維護用戶長鏈接:
- 1)每個用戶一條鏈接到Login服務,并按時間發心跳包給Login服務;
- 2)服務定時檢查用戶鏈接狀態和心跳包,比如發現2個心跳周期都沒收到心跳,就認為用戶掉線了(有假在線問題,有興趣同學可回貼討論)。
Login服務收到用戶登錄請求以后,驗證uid/cookie,如果成功就把這個用戶的登錄信息發送給online。
此過程主要記錄的信息包含:
- 1)uid(用戶id);
- 2)Login服務器IP/Port;
- 3)Route服務器的IP/Port。
如果用戶發送IM消息,先發送到Login,Login轉發給Route,Route根據服務的類型(SVID),發現是IM協議就發送給后端的IM服務。
Login對并發要求比較高,一般要支持TCP+UDP+Websocket幾種方式,單服務可以做到10-250萬之間。從服務穩定性角度觸發,建議是控制VM的CPU/內存,單服務器以20-50萬為合適。
Login服務器本身沒有狀態,任何一個Login服務斷掉,用戶端檢測到以后重連另一個Login服務器就可以了,對整體服務可靠性基本沒有影響。
7.4 Online服務
Online服務主要負責維護用戶的在線信息:
- 1)如果用戶掉線,Online服務里信息就是空;
- 2)如果用戶在線,Online就能找到用戶登錄在哪個集群,哪個Login服務器上。
Online業務相對簡單:多個Login服務器會連接到Online,定期同步用戶登錄和離線信息。
Online主要職責是:把用戶狀態信息存儲在Redis集群里。因此也是無狀態的,任何一個Online服務掛掉,不影響整體服務能力。
如果集群規模不大,用戶規模也不大,Online服務也可以收到Login服務里去。
如果規模比較大,建議分拆出來,一方面簡化Login的邏輯復雜度,同時避免寫Redis的慢操作放在Login服務里。因為Login要同時處理50萬以上的并發鏈接,不適合在循環里嵌入慢操作。
7.5 Route服務
Route服務的設計核心,是作為信令系統跟其它子系統的交互層。Route下接Login服務,可以接受用戶業務信息(IM),也可以往用戶推送下行消息。
多個后端業務系統可以接入到Route,按照服務類型(SVID, service id)注冊。比如IM服務可以接入到Route, 注冊SVID_IM。這樣Login接收到SVID=SVID_IM的消息,轉發給Route,Route就可以根據SVID轉發給IM相關的服務。
Route簡單的根據SVID做轉發,不處理具體的業務邏輯,因此也是無狀態的。一個信令集群可以有多個Route服務,任何服務掛了不影響整體服務能力。
8、推送系統
推送系統的核心任務:是接收到給用戶發送下行消息的請求以后,去信令服務查詢用戶是否在線,如果在線走信令推送,如果不在線走離線推送(如iOS的APNS、華為推送、小米推送等)。
因為推送服務可能出現大規模并發蜂擁,比如大群激烈討論的時候,會觸發億級的TPS。因此推送服務用Kafka做了削峰。
我在實際的技術實現上,將推送系統進行了如下細分:

具體就是:
- 1)PushProxy:接受用戶的推送請求,寫入Kafka;
- 2)Kafka:緩存推送服務;
- 3)PushServer:從Kafka獲取推送請求,判斷用戶是否在線;
- 4)PushWorker:真正推送給信令或者APNS,華為推送等。
這里同樣,除了Kafka以外每個服務都是無狀態的,因為也可以實現平行擴展和容錯,任何服務掛掉不影響整體服務可用性。
9、存儲系統
存儲服務主要是負責消息的存儲和查詢,因為消息量巨大,對存儲服務的并發能力和存儲量要求巨大。
為了平衡性能、空間和成本,存儲服務按數據的熱度進行了分級和區別對待。
具體是:
- 1)短期消息(7天):存儲在Redis里;
- 2)近期消息(1-3個月):存儲在Mysql里,以備用戶實時查詢;
- 3)歷史信息:存儲在HBase里,作為歷史數據慢查詢。
同時,為了應對超大群的大量消息處理,存儲服務在實際的技術實現上,也做了比較細的分拆。
存儲服務具體拆分如下圖:

具體的業務劃分就是:
- 1)MsgProxy:負責接受IM子系統的存儲請求,寫入Kafka;
- 2)MsgWriter:從Kafka獲取寫請求,按需寫入Redis和Mysql;
- 3)MsgReader:接受用戶的消息查詢請求,從Redis,Mysql或者HBase讀數據;
- 4)運維工具:主要是數據庫的運維需求。
消息隊列(Kafka)在這里角色比較重要,因為對于高并發請求(100萬人公眾號),需要通過消息隊列來做削峰和并行。
在具體部署上:可能是3-4個MsgProxy,后端可以對應15個左右的MsgWriter。MsgWriter是比較慢的,需要同時操作多個數據庫,還要保證操作的原子性。
10、本篇小結
本篇主要總結了這套億級用戶量IM系統的總體架構設計,為了高性能和橫向擴展性,基于微信的理念將整個架構在實現上分成了4個子系統,分別是:IM業務系統、信令系統、推送系統、存儲系統。
針對這4個子系統,在實際的技術應用層上,又進行了進一步的服務拆分和細化,使得整個架構伸縮性大大增強。
—— 下篇《一套億級用戶的IM架構技術干貨(下篇):可靠性、有序性、弱網優化等》稍后發布,敬請期待 ——
附錄:相關文章
《一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)》(* 力薦)
《一套原創分布式即時通訊(IM)系統理論架構方案》(* 力薦)
《從零到卓越:京東客服即時通訊系統的技術架構演進歷程》(* 力薦)
《現代IM系統中聊天消息的同步和存儲方案探討》(* 力薦)
《一套高可用、易伸縮、高并發的IM群聊、單聊架構方案設計實踐》(* 力薦)
《社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等》
《社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進》
《社交軟件紅包技術解密(三):微信搖一搖紅包雨背后的技術細節》
《社交軟件紅包技術解密(四):微信紅包系統是如何應對高并發的》
《社交軟件紅包技術解密(五):微信紅包系統是如何實現高可用性的》
《社交軟件紅包技術解密(六):微信紅包系統的存儲層架構演進實踐》
《社交軟件紅包技術解密(七):支付寶紅包的海量高并發技術實踐》
《從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路》(* 力薦)
《從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結》
《從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐》
《瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)》
《阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處》
《一套億級用戶的IM架構技術干貨(上篇):整體架構、服務拆分等》
>> 更多同類文章 ……
本文已同步發布于“即時通訊技術圈”公眾號。
▲ 本文在公眾號上的鏈接是:點此進入。同步發布鏈接是:http://www.52im.net/thread-3393-1-1.html
作者:Jack Jiang (點擊作者姓名進入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時通訊開發交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時是【原創Java
Swing外觀工程BeautyEye】和【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文
歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。