本文以用QQ好友系統(tǒng)為例,為大家分析QQ好友系統(tǒng)設(shè)計(jì)的原理以及設(shè)計(jì)步驟。閱讀完整文相信大家對(duì)設(shè)計(jì)QQ好友系統(tǒng)有了一定的認(rèn)識(shí)。
創(chuàng)新互聯(lián)專(zhuān)注于企業(yè)全網(wǎng)營(yíng)銷(xiāo)推廣、網(wǎng)站重做改版、嶧城網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5開(kāi)發(fā)、商城系統(tǒng)網(wǎng)站開(kāi)發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)網(wǎng)站制作、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁(yè)設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為嶧城等各大城市提供網(wǎng)站開(kāi)發(fā)制作服務(wù)。什么是好友系統(tǒng)?
簡(jiǎn)單的說(shuō),好友系統(tǒng)是維護(hù)用戶好友關(guān)系的系統(tǒng)。我們最熟悉的好友系統(tǒng)案例當(dāng)屬Q(mào)Q,實(shí)際上QQ是一款即時(shí)通訊工具,憑著好友系統(tǒng)沉淀了海量的好友關(guān)系鏈,從而鑄就了一個(gè)堅(jiān)不可摧的商業(yè)帝國(guó)。好友系統(tǒng)的重要性可見(jiàn)一斑。
熟悉互聯(lián)網(wǎng)產(chǎn)品的人都知道,當(dāng)產(chǎn)品有了一定的用戶量,往往會(huì)開(kāi)發(fā)一個(gè)好友系統(tǒng)。其主要目的是增加用戶粘性(有了好友就會(huì)常來(lái))或者增加社區(qū)活躍度(有了好友就會(huì)多交流)。
而我的后臺(tái)開(kāi)發(fā)生涯就是從這樣一個(gè)系統(tǒng)開(kāi)始的。
那時(shí)候,好友系統(tǒng)對(duì)于我們團(tuán)隊(duì)大部分人來(lái)說(shuō),都是一個(gè)全新的事物,因?yàn)槲覀兇蟛糠秩硕际菓?yīng)屆生。整個(gè)系統(tǒng)的架構(gòu)自然不是我們一群黃毛小孩所能創(chuàng)造。當(dāng)年的架構(gòu)圖已經(jīng)找不到了,但是憑著一點(diǎn)記憶和多年來(lái)的經(jīng)驗(yàn)積累,還是可以把當(dāng)年的架構(gòu)勾勒出來(lái)。
如圖,好友系統(tǒng)的架構(gòu)是常見(jiàn)的3層結(jié)構(gòu),包括接入層、邏輯層和數(shù)據(jù)層。
我們先從數(shù)據(jù)層講起。
因?yàn)槲覀儗?duì)QQ太熟悉了,我們可以很容易地列出好友系統(tǒng)的數(shù)據(jù)主要包括用戶資料、好友關(guān)系鏈、消息(聊天消息和系統(tǒng)消息)、在線狀態(tài)等。
互聯(lián)網(wǎng)產(chǎn)品往往要面對(duì)海量的請(qǐng)求并發(fā),傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)比較難滿足讀寫(xiě)需求。在存儲(chǔ)中,一般是讀多寫(xiě)少的數(shù)據(jù)才會(huì)使用MySQL等關(guān)系型數(shù)據(jù)庫(kù),而且往往還需要增加緩存來(lái)保證性能;NoSQL(Not Only SQL)應(yīng)該是目前的主流。
對(duì)于好友系統(tǒng),用戶資料和好友關(guān)系鏈都使用了kv存儲(chǔ),而消息使用公司自研的tlist(可以用redis的list替代),在線狀態(tài)下面再介紹。
接著是邏輯層。
在這個(gè)系統(tǒng)中復(fù)雜度最高的應(yīng)該是消息服務(wù)(而這個(gè)服務(wù)我并沒(méi)有參與開(kāi)發(fā)[捂臉])。
消息服務(wù)中,消息按類(lèi)型分為聊天消息和系統(tǒng)消息(系統(tǒng)消息包括加好友消息、全局tips推送等),按狀態(tài)分為在線消息和離線消息。在實(shí)現(xiàn)中,維護(hù)3種list:聊天消息、系統(tǒng)消息和離線消息。聊天消息是兩個(gè)用戶共享的,系統(tǒng)消息和離線消息每個(gè)用戶獨(dú)占。當(dāng)用戶在線時(shí),聊天消息和系統(tǒng)消息是直接發(fā)送的;如果用戶離線,就把消息往離線消息list存入一份,等用戶再次登錄時(shí)拉取。
這樣看來(lái),消息服務(wù)并不復(fù)雜?其實(shí)不然,系統(tǒng)設(shè)計(jì)中常規(guī)的流程設(shè)計(jì)往往是比較簡(jiǎn)單的,但是對(duì)于互聯(lián)網(wǎng)產(chǎn)品,異常情況才是常態(tài),當(dāng)把各種異常情況都考慮進(jìn)來(lái)時(shí),系統(tǒng)就會(huì)非常復(fù)雜。
這個(gè)例子中,消息發(fā)送丟包是一種異常情況,怎么保證在丟包情況下,還能正常運(yùn)行就是一個(gè)不小的問(wèn)題。
常見(jiàn)的解決方法是收包方回復(fù)確認(rèn)包,發(fā)送方如果沒(méi)收到確認(rèn)包就重發(fā)。但是確認(rèn)包又可能丟包,那又可以給確認(rèn)包增加一個(gè)確認(rèn)包,這是一個(gè)永無(wú)止境的確認(rèn)。
解決方法可以參考TCP的重傳機(jī)制。那問(wèn)題來(lái)了,我們?yōu)槭裁床挥肨CP呢?因?yàn)門(mén)CP還是比較慢的,聊天消息的可靠性沒(méi)有交易數(shù)據(jù)要求那么高,丟幾條消息并不會(huì)造成嚴(yán)重后果,但是如果用戶每次發(fā)送消息后都要等很久才能被收到,那體驗(yàn)是很差的。
一個(gè)比較折中的方案是,收包方回復(fù)確認(rèn)包,如果發(fā)送方在一定時(shí)間內(nèi)沒(méi)有收到確認(rèn)就重發(fā);如果收包方收到兩個(gè)相同的包(自定義seq一樣),去重即可。
一個(gè)面試題引發(fā)的討論:
面試時(shí)我常常會(huì)問(wèn)候選人一個(gè)問(wèn)題:在分布式系統(tǒng)中怎樣實(shí)現(xiàn)一個(gè)用戶同時(shí)只能有一個(gè)終端在線(用戶在兩個(gè)地方先后登錄賬號(hào),后一次登錄可以把前一次登錄踢下線)?這是互聯(lián)網(wǎng)產(chǎn)品中非常基礎(chǔ)的一個(gè)功能,考察的是候選人基本的架構(gòu)設(shè)計(jì)能力。
設(shè)計(jì)要先從接入服務(wù)器(下稱(chēng)接口機(jī))說(shuō)起。接口機(jī)是好友系統(tǒng)對(duì)外的窗口,主要功能是維護(hù)用戶連接、登錄鑒權(quán)、加解密數(shù)據(jù)和向后端服務(wù)透?jìng)鲾?shù)據(jù)等。用戶連接好友系統(tǒng),首先是連接到接口機(jī),鑒權(quán)成功后,接口機(jī)會(huì)在內(nèi)存中維護(hù)用戶session,后續(xù)的操作都是基于session進(jìn)行。
如圖所示,用戶如果嘗試登錄兩次,接口機(jī)通過(guò)session就可以將第一次的登錄踢下線,從而保證只有一個(gè)終端在線。
問(wèn)題解決了嗎?
沒(méi)有。因?yàn)閷?shí)際系統(tǒng)肯定不會(huì)只有一臺(tái)接口機(jī),在多臺(tái)接口的情況下,上面的方法就不可行了。因?yàn)槊總€(gè)接口機(jī)只能維護(hù)部分用戶的session,所以如果用戶先后連接到不同的接口機(jī),就會(huì)造成用戶多處登錄的問(wèn)題。
自然可以想到,解決的方法就是要維護(hù)一個(gè)用戶狀態(tài)的全局視圖。在我們的好友系統(tǒng)中,稱(chēng)為在線狀態(tài)服務(wù)。
在線狀態(tài)服務(wù),顧名思義就是維護(hù)用戶的在線狀態(tài)(登錄時(shí)間、接口機(jī)IP等)的服務(wù)。用戶登錄和退出會(huì)通過(guò)接口機(jī)觸發(fā)這里的狀態(tài)變更。因?yàn)榈卿洶屯顺霭伎赡軄G包,所以心跳包也用作在線狀態(tài)維護(hù)(收到一次心跳標(biāo)記為在線,收不到n次心跳標(biāo)記為離線)。
一種常用的方法是,采用bitmap存儲(chǔ)在線狀態(tài),具體是指在內(nèi)存中分配一塊空間,32位機(jī)器上的自然數(shù)一共有4294967296個(gè),如果用一個(gè)bit來(lái)表示一個(gè)用戶ID(例如QQ號(hào)),1代表在線,0代表離線,那么把全部自然數(shù)存儲(chǔ)在內(nèi)存只要4294967296 / (810241024) = 512MB(8bit = 1Byte)。當(dāng)然,實(shí)現(xiàn)中也可以根據(jù)需要給每個(gè)用戶分配更多的bit。
于是,踢下線功能如圖所示。
用戶登錄的時(shí)候,接口機(jī)首先查找本機(jī)上是否有session,如果有則更新session,接著給在線狀態(tài)服務(wù)發(fā)送登錄包,在線狀態(tài)服務(wù)檢查用戶是否已經(jīng)在線,如果在線則更新?tīng)顟B(tài)信息,并向上次登錄的接口機(jī)IP發(fā)送踢下線包;接口機(jī)在收到踢下線包時(shí)會(huì)檢查包中的用戶ID是否存在session,如果存在則給客戶端發(fā)送踢下線包并刪除session。
在實(shí)際中,踢下線功能還有很多細(xì)節(jié)問(wèn)題需要注意。
又回到用戶先后登錄同一臺(tái)接口機(jī)的情況:
圖中踢下線流程是正確的,但是如果步驟10和13調(diào)換了順序(在UDP傳輸中是常見(jiàn)的)會(huì)發(fā)生什么?大家可以自己推演一下,后到的踢下線包會(huì)把第二次登錄的A’踢下線了。這不是我們期望的。怎么辦呢?
解決方法分幾個(gè)細(xì)節(jié),①接口機(jī)在收到13號(hào)登錄成功包時(shí),先將session A替換成session A’,然后給客戶端A發(fā)生踢下線包(避免多處存活導(dǎo)致互相踢下線);②踢下線包中必須包含除用戶ID外的其他標(biāo)識(shí)信息,session的唯一標(biāo)識(shí)應(yīng)該是ID+XXX的形式(我最開(kāi)始采用的是ID+LoginTime),XXX是為了區(qū)分某次的登錄;③接口機(jī)在收到踢下線包的時(shí)候只要判斷ID+XXX是否吻合來(lái)決定是否給客戶端發(fā)踢下線包。
現(xiàn)實(shí)情況,問(wèn)題總是千奇百怪的,好在辦法總比問(wèn)題多。
比如我在項(xiàng)目中遇到過(guò)接口機(jī)和在線狀態(tài)服務(wù)時(shí)間漂移(差幾秒)的情況。這樣踢下線的唯一標(biāo)識(shí)就不能是用戶ID+LoginTime的形式了??梢詾槊看蔚牡卿浬梢粋€(gè)唯一的UUID解決。類(lèi)似的問(wèn)題還有很多,不再贅述。
總結(jié)一下,本篇主要介紹了好友系統(tǒng)的整體架構(gòu)和部分模塊的實(shí)現(xiàn)方式。分布式系統(tǒng)中各個(gè)模塊的實(shí)現(xiàn)其實(shí)并不難,難點(diǎn)主要在于應(yīng)對(duì)復(fù)雜網(wǎng)絡(luò)環(huán)境帶來(lái)的問(wèn)題(如丟包、時(shí)延等)和服務(wù)器異常帶來(lái)的問(wèn)題(如為了應(yīng)對(duì)服務(wù)器宕機(jī)會(huì)增加服務(wù)器冗余度,進(jìn)而又會(huì)引發(fā)其它問(wèn)題)。
看完這篇文章,你們學(xué)會(huì)設(shè)計(jì)QQ好友系統(tǒng)了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道,感謝各位的閱讀。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.cdcxhl.com,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。