本文章參考https://systeminterview.com/design-a-chat-system.php撕捍,有刪減晰骑。
在本章中梧乘,我們將探討聊天系統(tǒng)的設(shè)計(jì)戴差。幾乎每個(gè)人都使用聊天應(yīng)用程序。圖12-1顯示了市場(chǎng)上一些最流行的應(yīng)用程序咽笼。
聊天應(yīng)用為不同的人執(zhí)行不同的功能顷编。確定確切的要求是極其重要的。例如剑刑,當(dāng)面試官考慮一對(duì)一聊天時(shí)媳纬,您不希望設(shè)計(jì)一個(gè)專注于集體聊天的系統(tǒng)。探索功能需求是很重要的施掏。
步驟1-確定需求
當(dāng)然钮惠,世界上沒有最完美的架構(gòu),只有最合適的架構(gòu)七芭,也沒有所謂的通用方案素挽,不同的解決方案都有其優(yōu)缺點(diǎn),只有最滿足業(yè)務(wù)的系統(tǒng)才是一個(gè)好的系統(tǒng)狸驳。而且预明,在有限的人力、物力耙箍,綜合考慮時(shí)間成本撰糠,通常需要做出很多權(quán)衡。
就要設(shè)計(jì)的聊天應(yīng)用程序的類型達(dá)成一致至關(guān)重要辩昆。在市場(chǎng)上阅酪,有Facebook Messenger、微信和WhatsApp等一對(duì)一聊天應(yīng)用汁针,Slack等專注于群聊的office聊天應(yīng)用术辐,Discord等專注于大群體互動(dòng)和低語音聊天延遲的游戲聊天應(yīng)用。
第一組澄清問題應(yīng)該明確面試官在要求你設(shè)計(jì)聊天系統(tǒng)時(shí)的具體想法扇丛。至少术吗,弄清楚你是應(yīng)該專注于一對(duì)一聊天還是群聊應(yīng)用。您可能會(huì)問以下問題:
候選人:我們應(yīng)該設(shè)計(jì)什么樣的聊天應(yīng)用程序帆精?1對(duì)1還是基于組较屿?面試官:它應(yīng)該支持1對(duì)1和群聊。
候選人:這是一個(gè)移動(dòng)應(yīng)用程序嗎卓练?還是網(wǎng)絡(luò)應(yīng)用隘蝎?或者兩者都有?面試官:都有襟企。
候選人:這個(gè)應(yīng)用程序的規(guī)模是多少嘱么?創(chuàng)業(yè)應(yīng)用還是大規(guī)模?面試官:它應(yīng)該支持5000萬每日活躍用戶(DAU)顽悼。
候選人:對(duì)于群組聊天曼振,群組成員限制是多少几迄?面試官:最多100人
候選人:聊天應(yīng)用程序的重要功能是什么?它能支持附件嗎冰评?面試官:1對(duì)1聊天映胁,群聊,在線指示器甲雅。系統(tǒng)僅支持文本消息解孙。
候選人:郵件大小有限制嗎?面試官:是的抛人,文本長(zhǎng)度應(yīng)該少于100000個(gè)字符弛姜。
候選人:需要端到端加密嗎?面試官:現(xiàn)在不需要妖枚,但如果時(shí)間允許廷臼,我們會(huì)討論的。
候選人:我們應(yīng)該將聊天記錄存儲(chǔ)多久盅惜?面試官:永遠(yuǎn)中剩。
在本章中,我們將重點(diǎn)設(shè)計(jì)一款類似Facebook messenger的聊天應(yīng)用程序抒寂,重點(diǎn)介紹以下功能:
?一對(duì)一聊天,傳遞延遲低
?小組聊天(最多100人)
?在線狀態(tài)
?多設(shè)備支持掠剑。同一帳戶可以同時(shí)登錄到多個(gè)帳戶屈芜。
?推送通知
就設(shè)計(jì)規(guī)模達(dá)成一致也很重要。我們將設(shè)計(jì)一個(gè)支持5000萬DAU的系統(tǒng)朴译。
第2步-設(shè)計(jì)
為了開發(fā)高質(zhì)量的設(shè)計(jì)井佑,我們應(yīng)該具備客戶機(jī)和服務(wù)器如何通信的基本知識(shí)。在聊天系統(tǒng)中眠寿,客戶端可以是移動(dòng)應(yīng)用程序或web應(yīng)用程序躬翁。客戶機(jī)之間不直接通信盯拱。相反盒发,每個(gè)客戶端都連接到一個(gè)聊天服務(wù),該服務(wù)支持上述所有功能狡逢。讓我們關(guān)注基本業(yè)務(wù)宁舰。聊天室服務(wù)必須支持以下功能:
?接收來自其他客戶端的消息。
?為每條消息找到合適的收件人奢浑,并將消息轉(zhuǎn)發(fā)給收件人蛮艰。
?如果收件人不在線,則在服務(wù)器上保留該收件人的消息雀彼,直到其在線壤蚜。
圖12-2顯示了客戶端(發(fā)送方和接收方)與聊天服務(wù)之間的關(guān)系即寡。
當(dāng)客戶端打算啟動(dòng)聊天時(shí),它會(huì)使用一個(gè)或多個(gè)網(wǎng)絡(luò)協(xié)議連接聊天服務(wù)袜刷。對(duì)于聊天服務(wù)嘿悬,網(wǎng)絡(luò)協(xié)議的選擇很重要。讓我們和面試官討論一下水泉。
對(duì)于大多數(shù)客戶機(jī)/服務(wù)器應(yīng)用程序善涨,請(qǐng)求由客戶機(jī)發(fā)起。對(duì)于聊天應(yīng)用程序的發(fā)送方來說也是如此草则。在圖12-2中钢拧,當(dāng)發(fā)送方通過聊天服務(wù)向接收方發(fā)送消息時(shí),它使用經(jīng)過時(shí)間測(cè)試的HTTP協(xié)議炕横,這是最常見的web協(xié)議源内。在此場(chǎng)景中,客戶端打開與聊天服務(wù)的HTTP連接并發(fā)送消息份殿,通知服務(wù)將消息發(fā)送給接收方膜钓。keep-alive在這方面很有效,因?yàn)閗eep-alive頭允許客戶端與聊天服務(wù)保持持久連接卿嘲。它還減少了TCP握手的次數(shù)颂斜。HTTP在發(fā)送方是一個(gè)很好的選擇,許多流行的聊天應(yīng)用程序(如Facebook[1])最初使用HTTP發(fā)送消息拾枣。
然而沃疮,接收器端要復(fù)雜一些。由于HTTP是由客戶機(jī)發(fā)起的梅肤,因此從服務(wù)器發(fā)送消息并不簡(jiǎn)單司蔬。多年來,許多技術(shù)被用來模擬服務(wù)器啟動(dòng)的連接:輪詢姨蝴、長(zhǎng)輪詢和WebSocket俊啼。這些都是系統(tǒng)設(shè)計(jì)面試中廣泛使用的重要技巧,所以讓我們來研究一下它們左医。
輪詢
如圖12-3所示授帕,輪詢是一種客戶端定期詢問服務(wù)器是否有可用消息的技術(shù)。根據(jù)輪詢頻率炒辉,輪詢的成本可能會(huì)很高豪墅。它可能會(huì)消耗寶貴的服務(wù)器資源來回答一個(gè)在大多數(shù)情況下都沒有答案的問題。
長(zhǎng)輪詢
由于輪詢可能效率低下黔寇,下一步是長(zhǎng)輪詢(圖12-4)偶器。
在長(zhǎng)輪詢中,客戶端保持連接打開,直到有新消息可用或達(dá)到超時(shí)閾值屏轰。一旦客戶端接收到新消息颊郎,它會(huì)立即向服務(wù)器發(fā)送另一個(gè)請(qǐng)求,從而重新啟動(dòng)進(jìn)程霎苗。長(zhǎng)輪詢有幾個(gè)缺點(diǎn):
?發(fā)送方和接收方可能無法連接到同一聊天服務(wù)器姆吭。基于HTTP的服務(wù)器通常是無狀態(tài)的唁盏。如果使用循環(huán)法進(jìn)行負(fù)載平衡内狸,則接收消息的服務(wù)器可能與接收消息的客戶端沒有長(zhǎng)輪詢連接。
?服務(wù)器無法很好地判斷客戶端是否已斷開連接厘擂。
?效率低下昆淡。如果用戶聊天不多,長(zhǎng)輪詢?nèi)匀粫?huì)在超時(shí)后進(jìn)行定期連接刽严。
websocket長(zhǎng)連接
WebSocket是從服務(wù)器向客戶端發(fā)送異步更新的最常見解決方案昂灵。圖12-5顯示了其工作原理。
WebSocket連接由客戶端啟動(dòng)舞萄。它是雙向和持久的眨补。它從HTTP連接開始,可以通過一些定義良好的握手“升級(jí)”到WebSocket連接倒脓。通過這種持久連接撑螺,服務(wù)器可以向客戶端發(fā)送更新。即使有防火墻把还,WebSocket連接通常也能正常工作实蓬。這是因?yàn)樗鼈兪褂玫亩丝?0或443也被HTTP/HTTPS連接使用。
前面我們說過吊履,在發(fā)送方使用HTTP是一種很好的協(xié)議,但是由于WebSocket是雙向的调鬓,因此沒有強(qiáng)有力的技術(shù)理由不將其用于發(fā)送艇炎。圖12-6顯示了如何將WebSocket(ws)用于發(fā)送方和接收方。
通過將WebSocket用于發(fā)送和接收腾窝,它簡(jiǎn)化了設(shè)計(jì)缀踪,并使客戶端和服務(wù)器上的實(shí)現(xiàn)更加簡(jiǎn)單。由于WebSocket連接是持久的虹脯,因此高效的連接管理在服務(wù)器端至關(guān)重要驴娃。
高級(jí)設(shè)計(jì)
剛才我們提到WebSocket被選為客戶端和服務(wù)器之間雙向通信的主要通信協(xié)議,需要注意的是循集,其他一切都不必是WebSocket唇敞。事實(shí)上,聊天應(yīng)用程序的大多數(shù)功能(注冊(cè)、登錄疆柔、用戶配置文件等)都可以通過HTTP使用傳統(tǒng)的請(qǐng)求/響應(yīng)方法咒精。讓我們深入了解一下系統(tǒng)的高級(jí)組件。
如圖12-7所示旷档,聊天系統(tǒng)分為三大類:無狀態(tài)服務(wù)模叙、有狀態(tài)服務(wù)和第三方集成。
無狀態(tài)服務(wù)
無狀態(tài)服務(wù)是傳統(tǒng)的面向公眾的請(qǐng)求/響應(yīng)服務(wù)鞋屈,用于管理登錄范咨、注冊(cè)、用戶配置文件等厂庇。這些是許多網(wǎng)站和應(yīng)用程序的常見功能渠啊。
無狀態(tài)服務(wù)位于負(fù)載平衡器后面,負(fù)載平衡器的任務(wù)是根據(jù)請(qǐng)求路徑將請(qǐng)求路由到正確的服務(wù)宋列。這些服務(wù)可以是單一的或單個(gè)的微服務(wù)昭抒。我們不需要自己構(gòu)建這些無狀態(tài)服務(wù)中的許多,因?yàn)槭袌?chǎng)上存在可以輕松集成的服務(wù)炼杖。我們將深入討論的一項(xiàng)服務(wù)是服務(wù)發(fā)現(xiàn)灭返。它的主要工作是為客戶端提供一個(gè)可以連接到的聊天服務(wù)器的DNS主機(jī)名列表。
有狀態(tài)服務(wù)
唯一有狀態(tài)的服務(wù)是聊天服務(wù)坤邪。該服務(wù)是有狀態(tài)的熙含,因?yàn)槊總€(gè)客戶端都保持與聊天服務(wù)器的持久網(wǎng)絡(luò)連接。在該服務(wù)中艇纺,只要服務(wù)器仍然可用怎静,客戶端通常不會(huì)切換到另一個(gè)聊天服務(wù)器。服務(wù)發(fā)現(xiàn)與聊天服務(wù)密切配合黔衡,以避免服務(wù)器過載蚓聘。我們將深入探討細(xì)節(jié)。
第三方集成
對(duì)于聊天應(yīng)用程序盟劫,推送通知是最重要的第三方集成夜牡。這是一種在新消息到達(dá)時(shí)通知用戶的方法,即使應(yīng)用程序未運(yùn)行侣签。推送通知的正確集成至關(guān)重要塘装。有關(guān)更多信息,請(qǐng)參閱第10章設(shè)計(jì)通知系統(tǒng)影所。
可伸縮性
在小范圍內(nèi)蹦肴,上面列出的所有服務(wù)都可以安裝在一臺(tái)服務(wù)器中。即使按照我們?cè)O(shè)計(jì)的規(guī)模猴娩,理論上也可以在一臺(tái)現(xiàn)代云服務(wù)器中容納所有用戶連接阴幌。服務(wù)器可以處理的并發(fā)連接數(shù)很可能是限制因素勺阐。在我們的場(chǎng)景中,在一百萬并發(fā)用戶的情況下裂七,假設(shè)每個(gè)用戶連接在服務(wù)器上需要10K的內(nèi)存(這是一個(gè)非常粗略的數(shù)字皆看,并且非常依賴于語言選擇),它只需要大約10GB的內(nèi)存就可以在一個(gè)框中容納所有連接背零。
如果我們提出一個(gè)設(shè)計(jì)腰吟,所有的東西都放在一臺(tái)服務(wù)器上,這可能會(huì)在面試官的腦海中升起一個(gè)巨大的危險(xiǎn)信號(hào)徙瓶。沒有一個(gè)技術(shù)專家會(huì)在一臺(tái)服務(wù)器上設(shè)計(jì)這樣的規(guī)模毛雇。由于許多因素,單服務(wù)器設(shè)計(jì)是交易的破壞者侦镇。單點(diǎn)故障是其中最大的故障灵疮。
但是,從單一服務(wù)器設(shè)計(jì)開始是完全正確的壳繁。確保面試官知道這是一個(gè)起點(diǎn)震捣。將我們提到的所有內(nèi)容放在一起,圖12-8顯示了調(diào)整后的高級(jí)設(shè)計(jì)闹炉。
在圖12-8中蒿赢,客戶端保持與聊天服務(wù)器的持久WebSocket連接,以進(jìn)行實(shí)時(shí)消息傳遞渣触。
?聊天服務(wù)器便于發(fā)送/接收消息羡棵。
?狀態(tài)服務(wù)器管理在線/離線狀態(tài)。
?API服務(wù)器處理一切嗅钻,包括用戶登錄皂冰、注冊(cè)、更改配置文件等养篓。
?通知服務(wù)器發(fā)送推送通知秃流。
?最后,鍵值存儲(chǔ)用于存儲(chǔ)聊天歷史記錄柳弄。當(dāng)離線用戶聯(lián)機(jī)時(shí)剔应,她將看到以前的所有聊天記錄。
存儲(chǔ)
現(xiàn)在语御,我們已經(jīng)準(zhǔn)備好了服務(wù)器,服務(wù)正在運(yùn)行席怪,第三方集成已經(jīng)完成应闯。在技術(shù)層的深處是數(shù)據(jù)層。數(shù)據(jù)層通常需要一些努力才能使其正確挂捻。我們必須做出的一個(gè)重要決定是選擇正確的數(shù)據(jù)庫(kù)類型:關(guān)系數(shù)據(jù)庫(kù)還是NoSQL數(shù)據(jù)庫(kù)碉纺?為了做出明智的決定,我們將檢查數(shù)據(jù)類型和讀/寫模式。
典型的聊天系統(tǒng)中存在兩種類型的數(shù)據(jù)骨田。第一種是通用數(shù)據(jù)耿导,例如用戶配置文件、設(shè)置态贤、用戶好友列表舱呻。這些數(shù)據(jù)存儲(chǔ)在健壯可靠的關(guān)系數(shù)據(jù)庫(kù)中。復(fù)制和分片是滿足可用性和可伸縮性要求的常用技術(shù)悠汽。
第二個(gè)是聊天系統(tǒng)特有的:聊天歷史數(shù)據(jù)箱吕。理解讀/寫模式很重要。
?聊天系統(tǒng)的數(shù)據(jù)量巨大柿冲。此前的一項(xiàng)研究[2]顯示茬高,F(xiàn)acebook messenger和Whatsapp每天處理600億條消息。
?只經(jīng)常訪問最近的聊天假抄。用戶通常不會(huì)查找舊聊天記錄怎栽。
?盡管在大多數(shù)情況下都會(huì)查看最近的聊天記錄,但用戶可能會(huì)使用需要隨機(jī)訪問數(shù)據(jù)的功能宿饱,如搜索熏瞄、查看您的提及、跳轉(zhuǎn)到特定消息等刑棵。數(shù)據(jù)訪問層應(yīng)支持這些情況巴刻。
?1對(duì)1聊天應(yīng)用的讀寫比約為1:1。
選擇支持我們所有用例的正確存儲(chǔ)系統(tǒng)至關(guān)重要蛉签。出于以下原因胡陪,我們推薦鍵值存儲(chǔ):
?鍵值存儲(chǔ)允許輕松水平縮放。
?鍵值存儲(chǔ)提供非常低的數(shù)據(jù)訪問延遲碍舍。
?關(guān)系數(shù)據(jù)庫(kù)不能很好地處理長(zhǎng)尾[3]數(shù)據(jù)柠座。當(dāng)索引變大時(shí),隨機(jī)訪問的代價(jià)很高片橡。
?鍵值存儲(chǔ)被其他經(jīng)驗(yàn)證的可靠聊天應(yīng)用程序采用妈经。例如,F(xiàn)acebook messenger和Discord都使用鍵值存儲(chǔ)捧书。Facebook messenger使用HBase[4]吹泡,Discord使用Cassandra[5]。
數(shù)據(jù)模型
剛才经瓷,我們討論了使用鍵值存儲(chǔ)作為存儲(chǔ)層爆哑。最重要的數(shù)據(jù)是消息數(shù)據(jù)。讓我們仔細(xì)看看舆吮。
1對(duì)1聊天的消息表
圖12-9顯示了1對(duì)1聊天的消息表揭朝。主鍵是message_id队贱,它有助于確定消息順序。我們不能依靠created_at來決定消息序列潭袱,因?yàn)榭梢酝瑫r(shí)創(chuàng)建兩條消息柱嫌。
群組聊天的消息表
圖12-10顯示了群組聊天的消息表。復(fù)合主鍵是(通道id屯换、消息id)编丘。頻道和組在這里表示相同的含義。channel_id是分區(qū)鍵趟径,因?yàn)槿航M聊天中的所有查詢都在一個(gè)頻道中運(yùn)行瘪吏。
消息ID
如何生成消息id是一個(gè)值得探討的有趣話題。消息id負(fù)責(zé)確保消息的順序蜗巧。要確定消息的順序掌眠,消息id必須滿足以下兩個(gè)要求:
?ID必須是唯一的。
?ID應(yīng)按時(shí)間進(jìn)行排序幕屹,這意味著新行的ID高于舊行蓝丙。
我們?nèi)绾尾拍軐?shí)現(xiàn)這兩個(gè)保證?首先想到的是MySql中的“auto_increment”關(guān)鍵字望拖。然而渺尘,NoSQL數(shù)據(jù)庫(kù)通常不提供這樣的功能。
第二種方法是使用全局64位序列號(hào)生成器说敏,如Snowflake[6]鸥跟。這將在“第7章:在分布式系統(tǒng)中設(shè)計(jì)唯一的ID生成器”中討論。
最后一種方法是使用本地序列號(hào)生成器盔沫。本地意味著ID僅在組中是唯一的医咨。本地ID工作的原因是在一對(duì)一通道或組通道內(nèi)維護(hù)消息序列就足夠了。與全局ID實(shí)現(xiàn)相比架诞,這種方法更容易實(shí)現(xiàn)拟淮。
第3步-深度設(shè)計(jì)
在系統(tǒng)設(shè)計(jì)面試中,通常期望您深入了解高級(jí)設(shè)計(jì)中的一些組件谴忧。對(duì)于聊天系統(tǒng)很泊,服務(wù)發(fā)現(xiàn)、消息流和在線/離線指標(biāo)值得深入探索沾谓。
服務(wù)發(fā)現(xiàn)
服務(wù)發(fā)現(xiàn)的主要作用是根據(jù)地理位置委造、服務(wù)器容量等標(biāo)準(zhǔn)為客戶端推薦最佳的聊天服務(wù)器。Apache Zookeeper[7]是一種流行的服務(wù)發(fā)現(xiàn)開源解決方案均驶。它注冊(cè)所有可用的聊天服務(wù)器争涌,并根據(jù)預(yù)定義的標(biāo)準(zhǔn)為客戶端選擇最佳的聊天服務(wù)器。
圖12-11顯示了服務(wù)發(fā)現(xiàn)(Zookeeper)的工作原理辣恋。
用戶A嘗試登錄應(yīng)用程序亮垫。
負(fù)載平衡器將登錄請(qǐng)求發(fā)送到API服務(wù)器。
在后端對(duì)用戶進(jìn)行身份驗(yàn)證后伟骨,服務(wù)發(fā)現(xiàn)會(huì)為用戶A找到最佳的聊天服務(wù)器饮潦。在本例中,選擇了服務(wù)器2携狭,并將服務(wù)器信息返回給用戶A继蜡。
用戶A通過WebSocket連接到聊天服務(wù)器2。
消息流
了解聊天系統(tǒng)的端到端流程很有趣逛腿。在本節(jié)中稀并,我們將探討1對(duì)1聊天流、跨多個(gè)設(shè)備的消息同步以及群組聊天流单默。
1對(duì)1聊天消息流
圖12-12解釋了當(dāng)用戶A向用戶B發(fā)送消息時(shí)會(huì)發(fā)生什么碘举。
1.用戶A向聊天服務(wù)器1發(fā)送聊天信息\2.聊天服務(wù)器1從ID生成器獲取消息ID\3.聊天服務(wù)器1將消息發(fā)送到消息同步隊(duì)列\(zhòng)4.消息存儲(chǔ)在鍵值存儲(chǔ)中。5.a搁廓。如果用戶B在線引颈,則消息將轉(zhuǎn)發(fā)到用戶B連接的聊天服務(wù)器2。5.b境蜕。如果用戶B處于脫機(jī)狀態(tài)蝙场,則會(huì)從推送通知(PN)服務(wù)器發(fā)送推送通知\6.聊天服務(wù)器2將消息轉(zhuǎn)發(fā)給用戶B。用戶B和聊天服務(wù)器2之間存在持久的WebSocket連接粱年。
跨多個(gè)設(shè)備的消息同步
許多用戶有多臺(tái)設(shè)備售滤。我們將解釋如何跨多個(gè)設(shè)備同步消息。圖12-13顯示了消息同步的示例台诗。
在圖12-13中完箩,用戶A有兩個(gè)設(shè)備:電話和筆記本電腦。當(dāng)用戶A用手機(jī)登錄聊天應(yīng)用程序時(shí)拉庶,它會(huì)與聊天服務(wù)器1建立WebSocket連接嗜憔。類似地,筆記本電腦和聊天服務(wù)器1之間存在連接氏仗。
每個(gè)設(shè)備都維護(hù)一個(gè)名為cur_max_message_id的變量吉捶,該變量跟蹤設(shè)備上的最新消息id。滿足以下兩個(gè)條件的消息被視為新聞消息:
?收件人ID等于當(dāng)前登錄的用戶ID皆尔。
?鍵值存儲(chǔ)中的消息ID大于cur_max_Message_ID呐舔。
由于每個(gè)設(shè)備上都有不同的cur_max_message_id,消息同步很容易慷蠕,因?yàn)槊總€(gè)設(shè)備都可以從KV存儲(chǔ)中獲取新消息珊拼。
群聊消息流
與一對(duì)一聊天相比,群組聊天的邏輯更加復(fù)雜流炕。圖12-14和12-15解釋了流程澎现。
圖12-14解釋了用戶A在群聊中發(fā)送消息時(shí)發(fā)生的情況仅胞。假設(shè)組中有3個(gè)成員(用戶A、用戶B和用戶C)剑辫。首先干旧,將來自用戶A的郵件復(fù)制到每個(gè)組成員的郵件同步隊(duì)列:一個(gè)用于用戶B,另一個(gè)用于用戶C妹蔽。您可以將郵件同步隊(duì)列視為收件人的收件箱椎眯。此設(shè)計(jì)選擇適用于小團(tuán)體聊天,因?yàn)椋?/p>
?它簡(jiǎn)化了郵件同步流程胳岂,因?yàn)槊總€(gè)客戶端只需檢查自己的收件箱即可獲得新郵件编整。
?當(dāng)組號(hào)較小時(shí),在每個(gè)收件人的收件箱中存儲(chǔ)一份副本并不太昂貴乳丰。
微信采用了類似的方法掌测,它將一個(gè)群組的成員限制在500人[8]。但是成艘,對(duì)于具有大量用戶的組赏半,為每個(gè)成員存儲(chǔ)消息副本是不可接受的。
在收件人端淆两,收件人可以接收來自多個(gè)用戶的消息断箫。每個(gè)收件人都有一個(gè)收件箱(郵件同步隊(duì)列),其中包含來自不同發(fā)件人的郵件秋冰。圖12-15說明了設(shè)計(jì)仲义。
在線狀態(tài)
在線狀態(tài)指示器是許多聊天應(yīng)用程序的基本功能。通常剑勾,您可以在用戶的個(gè)人資料圖片或用戶名旁邊看到一個(gè)綠點(diǎn)埃撵。本節(jié)解釋了幕后發(fā)生的事情。
在高級(jí)設(shè)計(jì)中虽另,狀態(tài)服務(wù)器負(fù)責(zé)管理在線狀態(tài)并通過WebSocket與客戶端通信暂刘。有一些流將觸發(fā)聯(lián)機(jī)狀態(tài)更改。讓我們逐一檢查一下捂刺。
用戶登錄
“服務(wù)發(fā)現(xiàn)”部分解釋了用戶登錄流程谣拣。在客戶端和實(shí)時(shí)服務(wù)之間建立WebSocket連接后,用戶a的在線狀態(tài)和時(shí)間戳處的最后一次活動(dòng)將保存在KV存儲(chǔ)中族展。狀態(tài)指示器顯示用戶登錄后處于聯(lián)機(jī)狀態(tài)迂卢。
用戶注銷
當(dāng)用戶注銷時(shí)吓坚,它將通過用戶注銷流程甘苍,如圖12-17所示痴荐。KV商店中的聯(lián)機(jī)狀態(tài)更改為脫機(jī)。狀態(tài)指示器顯示用戶處于脫機(jī)狀態(tài)。
用戶斷開連接
我們都希望我們的互聯(lián)網(wǎng)連接是一致和可靠的宾茂。然而瓷马,情況并非總是如此;因此刻炒,我們必須在設(shè)計(jì)中解決這個(gè)問題决采。當(dāng)用戶斷開與internet的連接時(shí),客戶端和服務(wù)器之間的持久連接將丟失坟奥。處理用戶斷開連接的一種簡(jiǎn)單方法是將用戶標(biāo)記為脫機(jī),并在重新建立連接時(shí)將狀態(tài)更改為聯(lián)機(jī)拇厢。然而爱谁,這種方法有一個(gè)主要缺陷。用戶通常會(huì)在短時(shí)間內(nèi)頻繁斷開和重新連接到internet孝偎。例如访敌,當(dāng)用戶通過隧道時(shí),可以打開和關(guān)閉網(wǎng)絡(luò)連接衣盾。在每次斷開/重新連接時(shí)更新聯(lián)機(jī)狀態(tài)會(huì)使?fàn)顟B(tài)指示器頻繁更改寺旺,從而導(dǎo)致用戶體驗(yàn)不佳。
我們引入心跳機(jī)制來解決這個(gè)問題势决。在線客戶端定期向狀態(tài)服務(wù)器發(fā)送心跳事件阻塑。如果狀態(tài)服務(wù)器在特定時(shí)間內(nèi)(例如x秒)從客戶端接收到心跳事件,則認(rèn)為用戶處于聯(lián)機(jī)狀態(tài)果复。否則陈莽,它將處于脫機(jī)狀態(tài)。
在圖12-18中虽抄,客戶端每5秒向服務(wù)器發(fā)送一次心跳事件走搁。發(fā)送3個(gè)心跳事件后,客戶端斷開連接迈窟,并且在x=30秒內(nèi)未重新連接(任意選擇此數(shù)字以演示邏輯)私植。聯(lián)機(jī)狀態(tài)更改為脫機(jī)。
用戶在線狀態(tài)
用戶A的朋友如何知道狀態(tài)更改车酣?圖12-19解釋了其工作原理曲稼。狀態(tài)服務(wù)器使用發(fā)布-訂閱模型,其中每個(gè)好友對(duì)維護(hù)一個(gè)通道骇径。當(dāng)用戶A的聯(lián)機(jī)狀態(tài)更改時(shí)躯肌,它將事件發(fā)布到三個(gè)頻道,即頻道A-B破衔、A-C和A-D清女。這三個(gè)頻道分別由用戶B、C和D訂閱晰筛。因此嫡丙,朋友很容易獲得在線狀態(tài)更新拴袭。客戶端和服務(wù)器之間的通信是通過實(shí)時(shí)WebSocket進(jìn)行的曙博。
上述設(shè)計(jì)對(duì)于小用戶群是有效的拥刻。例如,微信采用了類似的方法父泳,因?yàn)槠溆脩羧荷舷逓?00般哼。對(duì)于較大的群體,通知所有成員在線狀態(tài)既昂貴又耗時(shí)惠窄。假設(shè)一個(gè)組有100000名成員蒸眠。每次狀態(tài)更改將生成100000個(gè)事件。要解決性能瓶頸杆融,一個(gè)可能的解決方案是僅當(dāng)用戶進(jìn)入組或手動(dòng)刷新好友列表時(shí)獲取聯(lián)機(jī)狀態(tài)楞卡。
第4步-技術(shù)總結(jié)
在本章中,我們介紹了一個(gè)聊天系統(tǒng)體系結(jié)構(gòu)脾歇,它支持1對(duì)1聊天和群聊蒋腮。WebSocket用于客戶端和服務(wù)器之間的實(shí)時(shí)通信。聊天系統(tǒng)包含以下組件:用于實(shí)時(shí)消息傳遞的聊天服務(wù)器藕各、用于管理在線狀態(tài)的狀態(tài)服務(wù)器池摧、用于發(fā)送推送通知的推送通知服務(wù)器、用于聊天歷史持久性的鍵值存儲(chǔ)以及用于其他功能的API服務(wù)器座韵。
如果你在面試結(jié)束時(shí)有多余的時(shí)間险绘,下面是一些額外的談話要點(diǎn):
?擴(kuò)展聊天應(yīng)用程序以支持照片和視頻等媒體文件。媒體文件的大小明顯大于文本誉碴。壓縮宦棺、云存儲(chǔ)和縮略圖是值得討論的有趣話題。
?端到端加密黔帕。Whatsapp支持對(duì)消息進(jìn)行端到端加密代咸。只有發(fā)件人和收件人才能閱讀郵件。感興趣的讀者應(yīng)參考參考資料[9]中的文章成黄。
?在客戶端緩存消息可以有效減少客戶端和服務(wù)器之間的數(shù)據(jù)傳輸呐芥。
?縮短加載時(shí)間。Slack構(gòu)建了一個(gè)地理分布的網(wǎng)絡(luò)奋岁,以緩存用戶的數(shù)據(jù)思瘟、通道等,從而縮短加載時(shí)間[10]闻伶。
?錯(cuò)誤處理滨攻。
–聊天服務(wù)器錯(cuò)誤。與聊天服務(wù)器的連接可能有幾十萬個(gè),甚至更多的持久連接光绕。如果聊天服務(wù)器脫機(jī)女嘲,服務(wù)發(fā)現(xiàn)(Zookeeper)將為客戶端提供一個(gè)新的聊天服務(wù)器,以建立新的連接诞帐。
–消息重新發(fā)送機(jī)制欣尼。重試和排隊(duì)是重新發(fā)送消息的常用技術(shù)。
OpenIM了解我們
OpenIMgithub開源地址:
https://github.com/OpenIMSDK/Open-IM-Server
OpenIM官網(wǎng) : https://www.rentsoft.cn
OpenIM官方論壇:https://forum.rentsoft.cn/
更多技術(shù)文章:
開源OpenIM:高性能停蕉、可伸縮愕鼓、易擴(kuò)展的即時(shí)通訊架構(gòu)https://forum.rentsoft.cn/thread/3
【OpenIM原創(chuàng)】簡(jiǎn)單輕松入門 一文講解WebRTC實(shí)現(xiàn)1對(duì)1音視頻通信原理https://forum.rentsoft.cn/thread/4
【OpenIM原創(chuàng)】開源OpenIM:輕量、高效慧起、實(shí)時(shí)拒啰、可靠、低成本的消息模型https://forum.rentsoft.cn/thread/1