IM序列9:淺談移動端IM的多點登陸和消息漫遊原理
1、前言
在移動端IM大行其道之前的PC端IM時代,所謂的多端登陸控制無非就是當你在一台電腦上登陸後,再在另一台電腦上登陸時就會把之前登陸的賬號給踢出(比如QQ就是這樣)。同樣PC端時代的IM消息漫遊不過就是可以在另一台電腦上下載之前的歷史記錄而已(參考PC端QQ)。
但自從Google在2013年的開發者大會(Google I/O)上公布了一款叫「Google 環聊」(網址:http://hangouts.google.com)的移動端IM後,憑藉Google的創意,多端登陸和消息漫遊才真正有了現在的體驗(Google環聊無法體驗的話,參考當前版本的微信、手機QQ就可以了):即允許同一賬號在多種設備上同時登陸和使用、且消息會以某種邏輯同步到另外的端上(包括自已發出的消息)等特性,使用得移動端IM的消息同步第一次真正的變的友好和易用。
本文將展開聊聊移動端IM「多點登陸」與「消息漫遊」的原理。
2、IM開發乾貨系列文章
《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》
《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》
《如何保證IM實時消息的「時序性」與「一致性」?》
《IM單聊和群聊中的在線狀態同步應該用「推」還是「拉」?》
《IM群聊消息如此複雜,如何保證不丟不重?》
《一種Android端IM智能心跳演算法的設計與實現探討(含樣例代碼)》
《移動端IM登錄時拉取數據如何作到省流量?》
《通俗易懂:基於集群的移動端IM接入層負載均衡方案分享》
《淺談移動端IM的多點登陸和消息漫遊原理》(本文)
本系列由公號「編碼前線」整理。
3、基本概念
什麼是多點登錄?
以微信為例:可以PC端、phone端同時登錄、同時收發消息。需要注意的是:一個端只能登錄一個實例,例如同一個QQ號,在pc1上登錄再到pc2上登錄,後者會把前者踢出,pc1會收到通知「你已在別處登錄xxoo」。
什麼是消息漫遊?
在任何一個終端的任何一個實例登錄qq,都能夠拉取到所有歷史聊天消息,這個就是消息漫遊。微信目前只支持「多點登錄」同時收發在線消息(如果你同時開過PC端微信和手機端微信,你可以注意到你收到和發出的消息都會在另一個端同時顯示出來),沒有實現「消息漫遊」,潛台詞是:登出手機微信,登錄PC微信,聊天,再登錄手機微信是看不到歷史消息的。
4、一個典型的IM消息收發架構
整個IM的架構可以抽象成這麼幾層:
1)客戶端:例如pc微信,手機qq
2)服務端:
2.1)入口層gate集群:能夠水平擴展,保持與客戶端的連接;
2.2)邏輯層logic、路由層router集群:高可用可擴展,實現業務邏輯,進行消息的路由;
2.3)cache:高可用cache集群,用來存儲用戶的在線狀態,與接入節點(用戶具體連接在哪個gate節點);
2.4)db:固化存儲消息,群信息,好友關係鏈等信息。
一個典型的消息收發流程如上圖步驟1-5:
1)用戶A登錄在gate1上,發出消息;
2)gate1將消息給logic/router;
3)logic/router查詢接收方的在線狀態(B在線,C不在線);
4)例如接收方C不在線,存儲離線;
4)例如接收方B在線,且登錄在gate2上,消息投遞給gate2;
5)gate2將消息投遞給B。
當然,單對單消息有一系列應用層超時、重傳、確認、去重的機制,這不是本文的重點,不進行展開,細節詳見《IM消息送達保證機制實現(一):保證在線實時消息的可靠投遞》、《IM消息送達保證機制實現(二):保證離線消息的可靠投遞》。
5、接收方多點登陸時的消息投遞原理
接收方多點登錄:pc登錄、phone也登錄,後一端登錄不會將前一端踢出,cache中存儲狀態與登錄點時,不再以user_id為key,改為以user_id+終端類型為key即可。
傳統的PC端IM時的B客戶端信息為:online(狀態),gate2(登錄點)。
在支持多端登陸的移動端IM時信息應改為:
B的pc客戶端:online(狀態),gate2(登錄點);
B的phone客戶端:online(狀態),gate3(登錄點)。
當用戶A給用戶B發送消息時,取出所有B的登錄點,進行消息群發即可(如上圖中步驟4與步驟5)。
6、發送方多點登陸時的消息投遞原理
有朋友可能要問,發送方和多點登錄有什麼關係?
假設用戶A登錄了兩個點:A1和A2;用戶B登錄了兩個點B1和B2:
A(A1發出的)發送消息給B(B1和B2);
B(B1發出的)發送消息給A(A1和A2)。
不就可以了么?
其實不然:A(A1發出的)發送消息給B(B1和B2),B(B1發出的)發送消息給A(A1和A2)。A2端雖然收到了所有B回復的消息,但消息其實是在A1端發出的,故A2端只知道聊天消息的一半(所有B的回復),缺失了聊天的上下文(所有A1端的發出)。故:如果發送方也進行了多點登錄,發送出去的任何消息,除了要投遞給多點登錄的接收方,還需要投遞給多點登錄的發送方(說白了就是自已發出的消息,也要發送到自已登陸的其它客戶端上)。
如上圖所示,發送方A和接收方B都進行了多點登陸,服務端cache中存儲的信息為:
A的pc端:online(狀態),gate0(登錄點);
A的phone端:online(狀態),gate1(登錄點);
B的pc端:online(狀態),gate2(登錄點);
B的phone端:online(狀態),gate3(登錄點)。
當用戶A(phone端)給用戶B發送消息時,除了要投遞給B的所有多點登錄端,還需要投遞給A自已多點登陸的其他端(pc端),如上圖中步驟4與步驟5。只有這樣,才能在所有用戶的所有端,恢復與還原雙方聊天的上下文。
7、消息漫遊原理
如果業務不需要支持「消息漫遊」的功能,對於在線消息,如果用戶實時接收到則是不需要存儲到資料庫的。但如果要支持「換一台機器(指的是用戶的客戶端)也能看到歷史的聊天消息」,就需要對所有消息進行存儲了。
消息投遞如上圖:用戶A發送消息給用戶B,雖然B在線,仍然要增加一個步驟2.5,在投遞之前進行存儲,以備B的其他端登陸時,可以拉取到歷史消息。
消息拉取如上圖:原本不在線的B(phone端),又重新登錄了,他怎麼拉取歷史消息?只需要在客戶端本地存儲一個上一次拉取到的msg_id(time),到服務端重新拉取即可。這裡還有個問題:由於服務端存儲所有消息成本是非常高的,所以一般「消息漫遊」是有時間(或者消息數)限制,不能拉取所有所有幾年前的歷史消息,比如只能拉取3個月內的雲端消息等。
8、本文小結
「多點登錄」是指多個端同時登錄一個帳號,同時收發消息,關鍵點是:
1)需要在服務端存儲同一個用戶多個端的狀態與登陸點;
2)發出消息時,要對發送方的多端與接收端的多端,都進行消息投遞。
「消息漫遊」是指一個用戶在任何端,都可以拉取到歷史消息,關鍵點是:
1)所有消息存儲在雲端;
2)每個端本地存儲last_msg_id,在登錄時可以到雲端同步歷史消息;
3)雲端存儲所有消息成本較高,一般會對歷史消息時間(或者條數)進行限制。
附:本系列文章參考「架構師之路」等網上文章
推薦閱讀:
※HTML5 實現錄音,然後上傳到伺服器,有現成方案嗎?
※HTML5 在手機上如何廣泛應用?技術層面好解決么?
※怎樣以簡單易懂方式向普通人解釋 HTML5 對 HTML 技術的改進?
※html中input框自動提示補全(H5)
※SegmentFault 技術周刊 Vol.20 - 用 Canvas 畫個星空