即时通讯系统架构设计-如何设计一款WhatsApp
本文譯自tech takshila經(jīng)OpenIM技術(shù)人員整理修訂后發(fā)布。
寫在前面
Open-IM是由前微信技術(shù)專家打造的開源的即時通訊組件。Open-IM包括IM服務(wù)端和客戶端SDK,實現(xiàn)了高性能、輕量級、易擴(kuò)展等重要特性。開發(fā)者通過集成Open-IM組件,并私有化部署服務(wù)端,可以將即時通訊、實時網(wǎng)絡(luò)能力快速集成到自身應(yīng)用中,并確保業(yè)務(wù)數(shù)據(jù)的安全性和私密性。
了解更多原創(chuàng)文章:
【OpenIM原創(chuàng)】開源OpenIM:輕量、高效、實時、可靠、低成本的消息模型
【OpenIM原創(chuàng)】C/C++調(diào)用golang函數(shù),golang回調(diào)C/C++函數(shù)
【OpenIM原創(chuàng)】簡單輕松入門 一文講解WebRTC實現(xiàn)1對1音視頻通信原理
【OpenIM擴(kuò)展】OpenIM服務(wù)發(fā)現(xiàn)和負(fù)載均衡golang插件:gRPC接入etcdv3
【開源OpenIM】高性能、可伸縮、易擴(kuò)展的即時通訊架構(gòu)
如果您有興趣可以在文章結(jié)尾了解到更多關(guān)于我們的信息,期待著與您的交流合作。
免責(zé)聲明:這是設(shè)計WhatsApp等即時通訊系統(tǒng)的一種方法。然而,我們不能保證WhatsApp是以這種方式設(shè)計的。這是我們設(shè)計類似系統(tǒng)的想法。
問題陳述
設(shè)計一個即時通訊平臺,如WhatsApp或Signal,用戶可以利用該平臺相互發(fā)送消息。應(yīng)用程序的一個重要方面是聊天信息不會永久存儲在應(yīng)用程序中。
*有趣的事實:*一些聊天信使(如FB Messenger)存儲聊天信息,除非用戶明確刪除它。然而,像WhatsApp這樣的即時通訊工具不會將消息永久保存在服務(wù)器上。
系統(tǒng)需求
instant messenger應(yīng)用程序應(yīng)滿足以下要求。
- 它應(yīng)該能夠支持用戶之間的一對一對話。
- 它應(yīng)該能夠向其他用戶顯示發(fā)送/交付/讀取確認(rèn)。
- 它應(yīng)該能夠提供關(guān)于用戶上次活動時間的信息。
- 它應(yīng)該允許用戶共享圖像。
- 它應(yīng)該能夠向其他用戶發(fā)送推送通知。
容量規(guī)劃
我們需要建立一個高度可擴(kuò)展的平臺,能夠支持WhatsApp規(guī)模的流量。此外,在進(jìn)行容量規(guī)劃時,我們需要確保考慮高峰流量的最壞情況。下面列出了我們可以用來估算應(yīng)用程序容量的一些數(shù)字(如WhatsApp)。
-每月應(yīng)用程序上的用戶數(shù):10億
-高峰流量時每秒的活動用戶數(shù):650000
-高峰流量時每秒的郵件數(shù):4000萬
整個應(yīng)用程序?qū)⒂蓭讉€微服務(wù)組成,每個微服務(wù)執(zhí)行特定的任務(wù)。處理聊天信息流量的數(shù)據(jù)平面(網(wǎng)站管理員包括到分布式系統(tǒng)章節(jié)的鏈接)(我們稱之為chat microservice)中所需的服務(wù)器數(shù)量可以使用以下等式進(jìn)行估計。
*#*𝑠𝑒𝑟𝑣𝑒𝑟𝑠 𝑖𝑛 𝑐?𝑎𝑡 𝑚𝑖𝑐𝑟𝑜𝑠𝑒𝑟𝑣𝑖𝑐𝑒= (#聊天信息/秒)__? 延遲)/#每臺服務(wù)器的并發(fā)連接
假設(shè)每臺服務(wù)器的并發(fā)連接數(shù)為100K,發(fā)送消息的延遲為20毫秒。在這種情況下,聊天服務(wù)器機(jī)隊中所需的服務(wù)器的估計數(shù)量(使用上述等式)將為8(即4000萬*20毫秒/100K)。在標(biāo)準(zhǔn)實踐中,建議再添加幾個服務(wù)器,以處理這些服務(wù)器可能的故障。在接下來的一節(jié)中,我們將看到這些聊天服務(wù)器對總體基礎(chǔ)設(shè)施成本的影響。
有趣的事實:在本次演講中,Rick Reed(軟件工程師@WhatsApp)談到了優(yōu)化基于Erlang的服務(wù)器應(yīng)用程序,以及調(diào)整FreeBSD內(nèi)核以支持每臺服務(wù)器數(shù)百萬個并發(fā)連接。這在很大程度上幫助了他們保持盡可能小的服務(wù)器占用空間。
高級設(shè)計
此即時通訊應(yīng)用程序所需的功能可以使用兩種微服務(wù)建模:聊天服務(wù)和臨時服務(wù)。聊天服務(wù)將為活躍用戶發(fā)送的在線聊天信息流量提供服務(wù)。該服務(wù)將檢查接收消息的用戶是否在線。如果用戶在線,則消息將立即轉(zhuǎn)發(fā)給該用戶。否則,消息將由臨時服務(wù)處理。此服務(wù)將負(fù)責(zé)維護(hù)發(fā)送給脫機(jī)用戶的所有消息(文本或圖像)。數(shù)據(jù)將暫時存儲在臨時存儲器中,直到脫機(jī)用戶恢復(fù)聯(lián)機(jī)。我們將在后面的一節(jié)中提供有關(guān)各個組件的更多詳細(xì)信息。
有趣的事實:WhatsApp實際上使用了一種非常類似的方法,正如同一位WhatsApp工程師(Rick Reed)在另一次演講中所討論的。
API設(shè)計
我們可以公開一個REST端點來與聊天服務(wù)交互。下面介紹了用于發(fā)送消息的API端點的定義。
sendMessage(String fromUser、String toUser、ClientMetaData ClientMetaData、String message)
請求:
_fromUser:\u發(fā)送請求的用戶ID
_toUser:\u向其發(fā)送請求的用戶ID
clientMetaData:用于存儲客戶端信息(如設(shè)備詳細(xì)信息、位置等)的元數(shù)據(jù)。
消息:作為通信的一部分發(fā)送的消息。
數(shù)據(jù)模型
我們將存儲詳細(xì)信息,例如用戶連接到的服務(wù)器以及用戶上次活動的時間。我們可以使用基于文檔的數(shù)據(jù)庫(如MongoDB)來存儲用戶信息,如用戶的最后活動時間(也稱為心跳時間)。數(shù)據(jù)模型將類似于下表。
表1:數(shù)據(jù)模型-用戶信息
組件圖
在本節(jié)中,我們將討論在一對一通信中發(fā)送消息的兩種不同場景。之后,我們將討論需要支持的其他功能,例如推送通知和用戶活動狀態(tài)。最后,我們將研究進(jìn)行優(yōu)化和處理故障場景的不同機(jī)制。
一對一通信
這里,我們將討論與向另一個用戶發(fā)送消息相關(guān)的兩種不同場景。第一種情況涉及向在線用戶發(fā)送文本消息。在第二個場景中,我們描述了向脫機(jī)用戶發(fā)送圖像所涉及的操作序列。
場景1:向在線用戶發(fā)送文本
下面介紹了向在線用戶發(fā)送文本消息的序列圖中每個步驟的詳細(xì)信息。
- 步驟1:Alice向Bob發(fā)送一條消息,該消息被定向到Alice連接的聊天服務(wù)器。
- 步驟2:Alice從其連接的聊天服務(wù)器(即聊天服務(wù)器A)獲得確認(rèn),消息標(biāo)記為Alice端發(fā)送。
- 步驟3:聊天服務(wù)器向數(shù)據(jù)存儲器發(fā)出請求,以獲取有關(guān)Bob連接的聊天服務(wù)器的信息。
- 步驟4:聊天室存儲返回Bob連接到聊天室服務(wù)器的信息。
- 步驟5:聊天室服務(wù)器A將消息轉(zhuǎn)發(fā)給聊天室服務(wù)器B。
- 步驟6:使用推送機(jī)制將消息傳遞給Bob。
- 步驟7:Bob將ACK發(fā)送回聊天服務(wù)器。
- 步驟8:ACK被轉(zhuǎn)發(fā)到Alice連接的聊天服務(wù)器A。
- 步驟9:ACK被傳遞給Alice,并被標(biāo)記為已傳遞。
- 步驟10:當(dāng)Bob閱讀消息時(假設(shè)15分鐘后),另一個ACK被發(fā)送到Chat_Server_B。
- 步驟11:聊天室服務(wù)器請求獲取Alice連接的服務(wù)器。
- 步驟12:聊天室存儲返回Alice連接到聊天室服務(wù)器的信息。
- 步驟13:聊天室服務(wù)器B將讀取確認(rèn)轉(zhuǎn)發(fā)給聊天室服務(wù)器A。
- 步驟14:將ACK轉(zhuǎn)發(fā)給Alice,將請求標(biāo)記為已讀。
場景2:向脫機(jī)用戶發(fā)送媒體文件
下面介紹了序列圖中向脫機(jī)用戶發(fā)送圖像的每個步驟的詳細(xì)信息。
- 步驟1:Alice向Bob發(fā)送一個圖像,該圖像被轉(zhuǎn)發(fā)到聊天服務(wù)器A,Alice與之連接的服務(wù)器。
- 步驟2:聊天服務(wù)器將圖像上傳到文件服務(wù)器,文件存儲在目錄結(jié)構(gòu)中
- 步驟3:文件服務(wù)器將上傳文件的圖像url返回給聊天室服務(wù)器。
- 步驟4:圖像url返回給Alice,用于在Alice的設(shè)備上呈現(xiàn)圖像。圖像被標(biāo)記為在Alice端發(fā)送。
- 步驟5:聊天服務(wù)器向Bob連接的服務(wù)器發(fā)出請求。
- 步驟6:聊天室存儲返回Bob離線的信息。
- 步驟7:聊天服務(wù)器將包含圖像url的消息轉(zhuǎn)發(fā)給臨時服務(wù)器
- 步驟8:臨時服務(wù)器將包含圖像url的消息存儲在臨時存儲器中。
- 第九步:Bob上線并與Chat_Server_B進(jìn)行心跳(網(wǎng)站管理員包括鏈接)。
- 步驟10:聊天服務(wù)器從臨時服務(wù)器獲取Bob的臨時消息。
- 步驟11:聊天服務(wù)器將臨時消息轉(zhuǎn)發(fā)給Bob。
- 步驟12:Bob從文件服務(wù)器獲取圖像。此時,映像將被傳送到Bob的設(shè)備,所有對瞬態(tài)消息的引用都將從系統(tǒng)中刪除。
- 步驟13:Bob的設(shè)備向聊天服務(wù)器發(fā)送Alice圖像的確認(rèn)
- 步驟14:獲取Alice連接到的服務(wù)器的信息;i、 e.聊天室服務(wù)器
- 步驟15:將確認(rèn)轉(zhuǎn)發(fā)至聊天室服務(wù)器
- 步驟16:將ACK傳遞給Alice,將消息標(biāo)記為已傳遞。
瞬態(tài)數(shù)據(jù)存儲
我們可以使用基于FIFO的策略實現(xiàn)基于隊列的機(jī)制來存儲和檢索瞬態(tài)消息。為此,我們可以使用現(xiàn)有的基于云的技術(shù),如AmazonSQS或WindowsAzure隊列服務(wù)。我們可以使用這些隊列來存儲發(fā)送給脫機(jī)用戶的臨時消息。一旦消息傳遞給脫機(jī)用戶,所有對這些臨時消息的引用都將從系統(tǒng)中刪除。
推送通知
使用推送技術(shù)向用戶傳遞消息有兩種方法:客戶端推送或服務(wù)器推送。如果我們沿著客戶機(jī)請求的路線走下去,我們可以在長輪詢和短輪詢之間做出決定。另一方面,有兩種方法可以實現(xiàn)服務(wù)器推送方法:WebSocket和服務(wù)器發(fā)送事件(SSE)。Websockets已經(jīng)成為聊天應(yīng)用程序事實上的通信協(xié)議。我們在下面的部分中提供了有關(guān)它的更多詳細(xì)信息。
使用輪詢技術(shù),客戶機(jī)定期向服務(wù)器請求新數(shù)據(jù)。選擇輪詢技術(shù)的權(quán)衡決定可以使用下面提到的數(shù)據(jù)點。
- 短輪詢:(例如,基于AJAX的計時器)
- *優(yōu)點:*如果請求之間的時間間隔較長,則更簡單且不太消耗服務(wù)器
- 缺點:不適用于需要以最小延遲通知服務(wù)器事件的情況
- 長輪詢:(例如,基于XHR的Comet)
- *優(yōu)點:*服務(wù)器事件的通知不會延遲
- *缺點:*更復(fù)雜,消耗更多服務(wù)器資源
將服務(wù)器消息推送到客戶端的方法主要有兩種類型。第一種是WebSocket,它是一種通信協(xié)議。它通過單個TCP連接提供雙工通信信道。由于雙向通信,它非常適合聊天應(yīng)用程序等場景。另一種稱為服務(wù)器發(fā)送事件(SSE),它允許服務(wù)器在建立初始客戶機(jī)-服務(wù)器連接后異步向客戶機(jī)發(fā)送“新數(shù)據(jù)”。SSE更適合發(fā)布者-訂閱者模型,如實時流式股票價格;twitter提供更新和瀏覽器通知。
用戶活動狀態(tài)
用戶最后一次處于活動狀態(tài)是可以在即時消息中找到的標(biāo)準(zhǔn)功能。我們在上面的表1中展示了存儲相關(guān)信息的數(shù)據(jù)模型
在圖4中,我們展示了使用WebSocket在客戶端和服務(wù)器之間維護(hù)連接的機(jī)制。一旦在客戶端和服務(wù)器之間建立了初始連接,通信就會切換到雙向二進(jìn)制協(xié)議。客戶端和服務(wù)器之間的連接使用心跳保持活動狀態(tài)。我們將上次從用戶接收心跳的時間存儲在數(shù)據(jù)庫中。然后可以查詢數(shù)據(jù)存儲以獲取用戶上次活動的時間。
優(yōu)化
我們可以使用下面列出的參數(shù)來建議系統(tǒng)中的優(yōu)化。在本文中,我們提供了建議系統(tǒng)優(yōu)化所應(yīng)遵循的方法的詳細(xì)信息。
- 延遲:我們可以使用分布式緩存(包括指向分布式緩存的鏈接),例如Redis,在內(nèi)存中緩存用戶活動狀態(tài)及其最近聊天的信息。這可能有助于減少應(yīng)用程序的總體延遲,并提供更好的客戶體驗。甚至一些數(shù)據(jù)庫解決方案也提供了內(nèi)存緩存解決方案,如Amazon DynamoDB Accelerator。
- **基礎(chǔ)設(shè)施成本:**從系統(tǒng)中可以明顯看出,聊天服務(wù)器對基礎(chǔ)設(shè)施成本的重要貢獻(xiàn)。如果不控制聊天服務(wù)器的足跡,那么聊天服務(wù)器產(chǎn)生的成本可能會迅速增加。一種方法是增加每個主機(jī)的連接數(shù)。這將大大減少維護(hù)服務(wù)所需的服務(wù)器數(shù)量。我們可以通過調(diào)整服務(wù)器配置和選擇合適的技術(shù)來完成這項任務(wù)。例如,WhatsApp的工程師通過優(yōu)化基于Erlang的服務(wù)器應(yīng)用程序和調(diào)優(yōu)FreeBSD內(nèi)核,能夠在每臺主機(jī)上實現(xiàn)數(shù)百萬個連接
- **可用性:**我們可以維護(hù)臨時消息的多個副本,這樣即使其中一個副本中的消息丟失,也可以從另一個副本中檢索。這意味著要維護(hù)這些臨時消息的副本。客戶端將負(fù)責(zé)從兩個隊列獲取消息,并將它們合并。我們將在下一節(jié)中討論更多內(nèi)容。
解決瓶頸
系統(tǒng)中更容易發(fā)生故障的主要瓶頸是聊天服務(wù)器和臨時存儲解決方案。在下面的部分中,我們推薦了一些處理此類故障的方法。
- 聊天服務(wù)器故障:系統(tǒng)中的聊天服務(wù)器將保持與用戶的連接。有兩種處理聊天服務(wù)器故障的方法。一種方法是將這些TCP連接傳輸?shù)搅硪慌_服務(wù)器;然而,這種故障轉(zhuǎn)移的實現(xiàn)并非微不足道。第二個相對簡單的方法是讓用戶客戶端在連接丟失的情況下自動啟動連接。用戶連接到的服務(wù)器的信息需要在數(shù)據(jù)庫中更新,這與我們采取的方法無關(guān)。例如,在圖5中,我們展示了處理此故障場景的示例。我們可以看到User1連接到Server1,當(dāng)該服務(wù)器關(guān)閉時,將重新建立與另一臺服務(wù)器(即Server2)的連接,并在數(shù)據(jù)庫中更新此信息。
- 瞬態(tài)存儲故障:瞬態(tài)存儲是另一個容易發(fā)生故障的組件,可能會導(dǎo)致脫機(jī)用戶在傳輸過程中丟失消息。我們可以復(fù)制每個用戶的臨時存儲,以防止在他們脫機(jī)時發(fā)送給他們的消息丟失。當(dāng)用戶重新聯(lián)機(jī)時,將查詢并合并用戶臨時存儲的原始實例和副本實例。在圖6中,我們展示了一種處理瞬時存儲故障的機(jī)制,該故障在用戶重新聯(lián)機(jī)時啟動。
監(jiān)測
我們希望確保我們的服務(wù)能夠以高可用性和低延遲滿足用戶需求。我們可以為這些指標(biāo)定義服務(wù)級別協(xié)議(SLA),并創(chuàng)建中度和重度監(jiān)控器,當(dāng)違反這些SLA時,這些監(jiān)控器會觸發(fā)警報。對于此應(yīng)用程序,我們可以為sendMessage API定義以下SLA。
- 可用性SLA:p99.999
- 延遲SLA:p99.99,共5毫秒
可用性SLA意味著,如果1000個請求中有1個以上失敗,監(jiān)控器將觸發(fā)警報。同樣,延遲SLA意味著,如果服務(wù)器對其接收的100個請求中超過1個請求的響應(yīng)時間超過5毫秒,則會觸發(fā)警報。
此外,我們可以在不同的錯誤場景中設(shè)置故障警報。當(dāng)聊天服務(wù)器無法從臨時存儲的所有副本中為用戶獲取臨時消息時,可能會出現(xiàn)這種情況。這映射到上面圖3所示的步驟#10,其中聊天服務(wù)器#B請求臨時服務(wù)器在Bob脫機(jī)時獲取發(fā)送給Bob的消息。讓我們假設(shè)我們在臨時存儲器中維護(hù)Bob消息的兩個副本,以使系統(tǒng)更加健壯。但是,由于臨時存儲的臨時問題,臨時服務(wù)器無法從兩個副本檢索消息。這是一個需要調(diào)試的錯誤場景,因此需要足夠的監(jiān)控警報。
擴(kuò)展要求
我們可以擴(kuò)展系統(tǒng)以支持群組聊天,通過群組聊天我們可以將消息傳遞給多個用戶。我們可以創(chuàng)建一個數(shù)據(jù)模型來存儲組數(shù)據(jù)實體,該實體將由GroupChatID標(biāo)識,并將用于維護(hù)屬于該組的人員列表。上述系統(tǒng)可擴(kuò)展以支持向在線和離線用戶發(fā)送消息的場景。我們可以構(gòu)建一個組件,該組件將負(fù)責(zé)確保根據(jù)組中所有用戶的活動狀態(tài)將消息傳遞給他們。
作為此問題的擴(kuò)展,可以涵蓋的另一個方面是安全性,特別是端到端加密,其中只有通信用戶可以讀取消息。每個用戶都有一個公鑰,該公鑰與該用戶正在通信的所有其他用戶共享。例如,兩個用戶Alice和Bob正在相互通信。Alice擁有Bob的公鑰,反之亦然;但是,它們的私鑰不共享。當(dāng)Alice向Bob發(fā)送消息時,該消息使用Bob的公鑰加密并通過網(wǎng)絡(luò)發(fā)送。服務(wù)器將加密的消息定向給Bob,Bob使用私鑰解密消息。這樣,服務(wù)器只能訪問加密的消息,只有Alice和Bob才能讀取他們交換的實際消息。
OpenIM github開源地址:
https://github.com/OpenIMSDK/Open-IM-Server
OpenIM官網(wǎng) : https://www.rentsoft.cn
OpenIM官方論壇: https://forum.rentsoft.cn/
我們致力于通過開源模式,為全球企業(yè)/開發(fā)者提供簡單、易用、高效的IM服務(wù)和實時音視頻通訊能力,幫助開發(fā)者降低項目的開發(fā)成本,并讓開發(fā)者掌控業(yè)務(wù)的核心數(shù)據(jù)。
IM作為核心業(yè)務(wù)數(shù)據(jù),安全的重要性毋庸置疑,OpenIM開源以及私有化部署讓企業(yè)能更放心使用。
如今IM云服務(wù)商收費高企,如何讓企業(yè)低成本、安全、可靠接入IM服務(wù),是OpenIM的歷史使命,也是我們前進(jìn)的方向。
如您有技術(shù)上面的高見請到我們的論壇聯(lián)系溝通,用戶也可與我們的技術(shù)人員談討使用方面的難題以及見解
總結(jié)
以上是生活随笔為你收集整理的即时通讯系统架构设计-如何设计一款WhatsApp的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python实现艾宾浩斯抗遗忘曲线(记忆
- 下一篇: 即时通讯系统集成开发