系統(tǒng)設計:設計類似 WhatsApp 的應用
在這個系統(tǒng)設計場景中,我們被要求設計一個類似WhatsApp的消息應用程序。
雖然在實際的討論中可能會重點討論該應用程序的一個或多個功能,但在本文中,我們將對系統(tǒng)的架構進行一個高層次的概述,然后可以根據(jù)需要深入探討具體的領域。
明確功能需求
通過向業(yè)務方提問來縮小范圍,因為在一個小時內(nèi)設計整個WhatsApp平臺是不現(xiàn)實的:
主要用例: 該應用的主要目的是發(fā)送、檢查和接收消息,以及閱讀和標記消息為已讀。
群組: 我們將不涉及群組消息,只考慮一對一消息。?內(nèi)容類型: 我們只支持文本消息,不支持圖片或視頻。
明確非功能需求
規(guī)模: 首先,我們來談談規(guī)模,即系統(tǒng)的大小和消息的處理量。假設每天發(fā)送100億條消息,并且我們計劃在一年內(nèi)將其翻倍。
可用性: 在可用性方面,我們希望系統(tǒng)高度可用并始終運行。
延遲: 至于系統(tǒng)延遲,我們希望它幾乎是即時的,因此大多數(shù)API請求應在100毫秒內(nèi)完成。
估算:數(shù)據(jù)計算
每天100億條消息,約為10B消息 / 86,400秒每天 = 115,740條消息每秒(MPS)。在一年內(nèi)翻倍意味著我們應計劃為115,740 * 2 = 231,480 MPS。
假設每條消息200字節(jié),每日存儲量為10B消息 * 200字節(jié) = 2 TB。年存儲量及增長約為2TB * 365天 * 2 = 1.5 PB。
需要注意的是,我們計算的是平均值,但系統(tǒng)需要處理高峰流量,這可能比平均MPS高得多。我們可能需要根據(jù)高峰時間進行擴展。
接口API設計
我們可能會使用RESTful API風格以獲得更廣泛的兼容性。以下是可能的端點細分:
- 發(fā)送消息 (POST /messages): 請求體包括接收者的ID和消息內(nèi)容。成功響應(200)返回唯一的消息標識符。錯誤代碼(400, 500)處理缺少參數(shù)或服務器問題。
- 檢查新消息 (GET /messages): 響應為包含未讀消息的數(shù)組(200)或如果沒有則返回204。
- 獲取特定消息 (GET /messages/:messageId): 返回特定消息(200)或未找到則返回404。
- 標記消息為已讀 (PUT或PATCH /messages/:messageId): 成功響應(200)確認更改,而404表示未找到消息。
其他考慮: 我們將集成WebSockets以實現(xiàn)實時更新。API將處理認證和初始連接建立。并且分頁可能是‘檢查新消息’端點所需的。安全措施,如輸入驗證,也是必要的。
系統(tǒng)設計
移動應用:用戶的主要界面將是移動應用(iOS, Android)。此應用程序處理發(fā)送和接收消息、聯(lián)系人管理和對話。
負載均衡器:為了有效處理傳入請求,我們將使用負載均衡器來分配流量到多個服務器。這樣可以提高應用程序的可靠性。
API服務器:所有請求將進入API服務器,這些服務器處理我們之前概述的RESTful API,管理消息邏輯。API服務器本身可以是無狀態(tài)的,這樣我們可以水平擴展(添加更多服務器)以應對流量增長。
WebSocket連接:類似WhatsApp的應用程序嚴重依賴WebSockets進行實時通信。聊天服務器將與移動應用程序保持持久的WebSocket連接。當消息到達時,可以立即推送到接收者的設備。
消息分發(fā)器:接下來,我們將有一個消息分發(fā)器服務,該服務的主要目的是將API服務器與直接數(shù)據(jù)庫寫入解耦,這對于處理高寫入量尤其重要。
消息隊列,如Kafka或RabbitMQ,是這里的理想選擇。其工作原理如下:
- API服務器接收到“發(fā)送消息”的POST請求。
- 它將消息放在隊列中,并迅速向客戶端返回成功/確認。
- 獨立的工作進程異步從隊列中讀取并將消息寫入數(shù)據(jù)庫。
數(shù)據(jù)庫 (NoSQL): 我們同意最終一致性是可以接受的,這使得NoSQL成為高消息量的可擴展選擇。
以下是兩個強有力的選擇:
- Cassandra: 以可擴展性、高可用性和寫性能而聞名的寬列存儲。特別適合我們預期的高寫入量和簡單讀取模式(主要通過ID獲取消息)。
- DynamoDB: AWS提供的完全托管的鍵值和文檔數(shù)據(jù)庫。如果我們想要一個最小維護的數(shù)據(jù)庫解決方案且能輕松擴展,這非常有利。
分片和分區(qū): 由于沒有單一數(shù)據(jù)庫可以處理我們1.5 PB的存儲需求,因此分片(水平分區(qū))數(shù)據(jù)是至關重要的。
但是我們將如何分片和分區(qū)這些數(shù)據(jù),以及這些API服務器如何知道從哪里請求這些數(shù)據(jù)?
我們可以基于userId進行分區(qū)。所有涉及用戶的消息將駐留在同一個分片/分區(qū)中。而我們的API服務器有兩種可能的方法來定位數(shù)據(jù):
- 一致性哈希環(huán): 數(shù)據(jù)位置可以基于分區(qū)鍵確定,允許API服務器直接路由請求到正確的數(shù)據(jù)庫分片。
- 元數(shù)據(jù)服務: 一個獨立的服務保持分區(qū)鍵到分片位置的映射。API服務器首先查詢此服務,然后進行數(shù)據(jù)庫調(diào)用。
結論和當前系統(tǒng)瓶頸
這概述了類似WhatsApp應用程序的主要架構。現(xiàn)在,讓我們看看我們當前系統(tǒng)中的潛在瓶頸和改進領域:
- 數(shù)據(jù)庫寫入: 高寫入量是一個潛在的瓶頸。分片、消息隊列和優(yōu)化的數(shù)據(jù)庫選擇是關鍵。
- 端到端加密: WhatsApp模型非常強調(diào)安全性。實現(xiàn)端到端加密將是一個關鍵討論點。
- 群組聊天: 此功能為消息路由和存儲帶來了額外的復雜性。
- 媒體處理: 我們可以實現(xiàn)一個處理圖像和視頻上傳的系統(tǒng),這里使用壓縮以及多種存儲大小的縮略圖。