溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何設(shè)計QQ好友系統(tǒng)

發(fā)布時間:2020-06-03 21:04:06 來源:億速云 閱讀:550 作者:Leah 欄目:建站服務(wù)器

本文以用QQ好友系統(tǒng)為例,為大家分析QQ好友系統(tǒng)設(shè)計的原理以及設(shè)計步驟。閱讀完整文相信大家對設(shè)計QQ好友系統(tǒng)有了一定的認(rèn)識。

什么是好友系統(tǒng)?

簡單的說,好友系統(tǒng)是維護(hù)用戶好友關(guān)系的系統(tǒng)。我們最熟悉的好友系統(tǒng)案例當(dāng)屬Q(mào)Q,實(shí)際上QQ是一款即時通訊工具,憑著好友系統(tǒng)沉淀了海量的好友關(guān)系鏈,從而鑄就了一個堅(jiān)不可摧的商業(yè)帝國。好友系統(tǒng)的重要性可見一斑。

熟悉互聯(lián)網(wǎng)產(chǎn)品的人都知道,當(dāng)產(chǎn)品有了一定的用戶量,往往會開發(fā)一個好友系統(tǒng)。其主要目的是增加用戶粘性(有了好友就會常來)或者增加社區(qū)活躍度(有了好友就會多交流)。

而我的后臺開發(fā)生涯就是從這樣一個系統(tǒng)開始的。

那時候,好友系統(tǒng)對于我們團(tuán)隊(duì)大部分人來說,都是一個全新的事物,因?yàn)槲覀兇蟛糠秩硕际菓?yīng)屆生。整個系統(tǒng)的架構(gòu)自然不是我們一群黃毛小孩所能創(chuàng)造。當(dāng)年的架構(gòu)圖已經(jīng)找不到了,但是憑著一點(diǎn)記憶和多年來的經(jīng)驗(yàn)積累,還是可以把當(dāng)年的架構(gòu)勾勒出來。
如何設(shè)計QQ好友系統(tǒng)

如圖,好友系統(tǒng)的架構(gòu)是常見的3層結(jié)構(gòu),包括接入層、邏輯層和數(shù)據(jù)層。

我們先從數(shù)據(jù)層講起。

因?yàn)槲覀儗Q太熟悉了,我們可以很容易地列出好友系統(tǒng)的數(shù)據(jù)主要包括用戶資料、好友關(guān)系鏈、消息(聊天消息和系統(tǒng)消息)、在線狀態(tài)等。

互聯(lián)網(wǎng)產(chǎn)品往往要面對海量的請求并發(fā),傳統(tǒng)的關(guān)系型數(shù)據(jù)庫比較難滿足讀寫需求。在存儲中,一般是讀多寫少的數(shù)據(jù)才會使用MySQL等關(guān)系型數(shù)據(jù)庫,而且往往還需要增加緩存來保證性能;NoSQL(Not Only SQL)應(yīng)該是目前的主流。

對于好友系統(tǒng),用戶資料和好友關(guān)系鏈都使用了kv存儲,而消息使用公司自研的tlist(可以用redis的list替代),在線狀態(tài)下面再介紹。

接著是邏輯層。

在這個系統(tǒng)中復(fù)雜度最高的應(yīng)該是消息服務(wù)(而這個服務(wù)我并沒有參與開發(fā)[捂臉])。

消息服務(wù)中,消息按類型分為聊天消息和系統(tǒng)消息(系統(tǒng)消息包括加好友消息、全局tips推送等),按狀態(tài)分為在線消息和離線消息。在實(shí)現(xiàn)中,維護(hù)3種list:聊天消息、系統(tǒng)消息和離線消息。聊天消息是兩個用戶共享的,系統(tǒng)消息和離線消息每個用戶獨(dú)占。當(dāng)用戶在線時,聊天消息和系統(tǒng)消息是直接發(fā)送的;如果用戶離線,就把消息往離線消息list存入一份,等用戶再次登錄時拉取。

這樣看來,消息服務(wù)并不復(fù)雜?其實(shí)不然,系統(tǒng)設(shè)計中常規(guī)的流程設(shè)計往往是比較簡單的,但是對于互聯(lián)網(wǎng)產(chǎn)品,異常情況才是常態(tài),當(dāng)把各種異常情況都考慮進(jìn)來時,系統(tǒng)就會非常復(fù)雜。

這個例子中,消息發(fā)送丟包是一種異常情況,怎么保證在丟包情況下,還能正常運(yùn)行就是一個不小的問題。

常見的解決方法是收包方回復(fù)確認(rèn)包,發(fā)送方如果沒收到確認(rèn)包就重發(fā)。但是確認(rèn)包又可能丟包,那又可以給確認(rèn)包增加一個確認(rèn)包,這是一個永無止境的確認(rèn)。

解決方法可以參考TCP的重傳機(jī)制。那問題來了,我們?yōu)槭裁床挥肨CP呢?因?yàn)門CP還是比較慢的,聊天消息的可靠性沒有交易數(shù)據(jù)要求那么高,丟幾條消息并不會造成嚴(yán)重后果,但是如果用戶每次發(fā)送消息后都要等很久才能被收到,那體驗(yàn)是很差的。

一個比較折中的方案是,收包方回復(fù)確認(rèn)包,如果發(fā)送方在一定時間內(nèi)沒有收到確認(rèn)就重發(fā);如果收包方收到兩個相同的包(自定義seq一樣),去重即可。

一個面試題引發(fā)的討論:

面試時我常常會問候選人一個問題:在分布式系統(tǒng)中怎樣實(shí)現(xiàn)一個用戶同時只能有一個終端在線(用戶在兩個地方先后登錄賬號,后一次登錄可以把前一次登錄踢下線)?這是互聯(lián)網(wǎng)產(chǎn)品中非常基礎(chǔ)的一個功能,考察的是候選人基本的架構(gòu)設(shè)計能力。

設(shè)計要先從接入服務(wù)器(下稱接口機(jī))說起。接口機(jī)是好友系統(tǒng)對外的窗口,主要功能是維護(hù)用戶連接、登錄鑒權(quán)、加解密數(shù)據(jù)和向后端服務(wù)透傳數(shù)據(jù)等。用戶連接好友系統(tǒng),首先是連接到接口機(jī),鑒權(quán)成功后,接口機(jī)會在內(nèi)存中維護(hù)用戶session,后續(xù)的操作都是基于session進(jìn)行。
如何設(shè)計QQ好友系統(tǒng)

如圖所示,用戶如果嘗試登錄兩次,接口機(jī)通過session就可以將第一次的登錄踢下線,從而保證只有一個終端在線。

問題解決了嗎?

沒有。因?yàn)閷?shí)際系統(tǒng)肯定不會只有一臺接口機(jī),在多臺接口的情況下,上面的方法就不可行了。因?yàn)槊總€接口機(jī)只能維護(hù)部分用戶的session,所以如果用戶先后連接到不同的接口機(jī),就會造成用戶多處登錄的問題。
如何設(shè)計QQ好友系統(tǒng)

自然可以想到,解決的方法就是要維護(hù)一個用戶狀態(tài)的全局視圖。在我們的好友系統(tǒng)中,稱為在線狀態(tài)服務(wù)。

在線狀態(tài)服務(wù),顧名思義就是維護(hù)用戶的在線狀態(tài)(登錄時間、接口機(jī)IP等)的服務(wù)。用戶登錄和退出會通過接口機(jī)觸發(fā)這里的狀態(tài)變更。因?yàn)榈卿洶屯顺霭伎赡軄G包,所以心跳包也用作在線狀態(tài)維護(hù)(收到一次心跳標(biāo)記為在線,收不到n次心跳標(biāo)記為離線)。

一種常用的方法是,采用bitmap存儲在線狀態(tài),具體是指在內(nèi)存中分配一塊空間,32位機(jī)器上的自然數(shù)一共有4294967296個,如果用一個bit來表示一個用戶ID(例如QQ號),1代表在線,0代表離線,那么把全部自然數(shù)存儲在內(nèi)存只要4294967296 / (8 1024 1024) = 512MB(8bit = 1Byte)。當(dāng)然,實(shí)現(xiàn)中也可以根據(jù)需要給每個用戶分配更多的bit。

于是,踢下線功能如圖所示。
如何設(shè)計QQ好友系統(tǒng)

用戶登錄的時候,接口機(jī)首先查找本機(jī)上是否有session,如果有則更新session,接著給在線狀態(tài)服務(wù)發(fā)送登錄包,在線狀態(tài)服務(wù)檢查用戶是否已經(jīng)在線,如果在線則更新狀態(tài)信息,并向上次登錄的接口機(jī)IP發(fā)送踢下線包;接口機(jī)在收到踢下線包時會檢查包中的用戶ID是否存在session,如果存在則給客戶端發(fā)送踢下線包并刪除session。

在實(shí)際中,踢下線功能還有很多細(xì)節(jié)問題需要注意。

又回到用戶先后登錄同一臺接口機(jī)的情況:

如何設(shè)計QQ好友系統(tǒng)
圖中踢下線流程是正確的,但是如果步驟10和13調(diào)換了順序(在UDP傳輸中是常見的)會發(fā)生什么?大家可以自己推演一下,后到的踢下線包會把第二次登錄的A’踢下線了。這不是我們期望的。怎么辦呢?

解決方法分幾個細(xì)節(jié),①接口機(jī)在收到13號登錄成功包時,先將session A替換成session A’,然后給客戶端A發(fā)生踢下線包(避免多處存活導(dǎo)致互相踢下線);②踢下線包中必須包含除用戶ID外的其他標(biāo)識信息,session的唯一標(biāo)識應(yīng)該是ID+XXX的形式(我最開始采用的是ID+LoginTime),XXX是為了區(qū)分某次的登錄;③接口機(jī)在收到踢下線包的時候只要判斷ID+XXX是否吻合來決定是否給客戶端發(fā)踢下線包。

現(xiàn)實(shí)情況,問題總是千奇百怪的,好在辦法總比問題多。

比如我在項(xiàng)目中遇到過接口機(jī)和在線狀態(tài)服務(wù)時間漂移(差幾秒)的情況。這樣踢下線的唯一標(biāo)識就不能是用戶ID+LoginTime的形式了??梢詾槊看蔚牡卿浬梢粋€唯一的UUID解決。類似的問題還有很多,不再贅述。

總結(jié)一下,本篇主要介紹了好友系統(tǒng)的整體架構(gòu)和部分模塊的實(shí)現(xiàn)方式。分布式系統(tǒng)中各個模塊的實(shí)現(xiàn)其實(shí)并不難,難點(diǎn)主要在于應(yīng)對復(fù)雜網(wǎng)絡(luò)環(huán)境帶來的問題(如丟包、時延等)和服務(wù)器異常帶來的問題(如為了應(yīng)對服務(wù)器宕機(jī)會增加服務(wù)器冗余度,進(jìn)而又會引發(fā)其它問題)。

看完這篇文章,你們學(xué)會設(shè)計QQ好友系統(tǒng)了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI