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