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