系統設計面試:設計WhatsApp
「設計WhatsApp」考驗的是大規模下的即時傳遞、持久化的訊息儲存、狀態(上線/最後上線時間),以及讓數億台裝置保持連線的連線管理問題。和以往一樣,面試官想要的是你的推理與取捨,而不是真實系統的複製品。
按部就班地推進:先釐清需求,再估算規模,設計連線與傳遞路徑,決定訊息如何儲存,然後處理群組聊天以及離線傳遞、順序性這類棘手的邊界情況。以下的解析遵循這條路徑。
1. 釐清需求
先固定範圍。核心是具有可靠傳遞的一對一訊息;要釐清群組聊天、媒體與端對端加密是否在本輪的範圍之內。
- 功能性:發送/接收訊息、已送達與已讀回條、上線狀態、群組聊天、收件人離線時的離線傳遞。
- 非功能性:低延遲、高可用性、持久性(不遺失訊息)、同一對話內的順序性。
- 需要澄清:端對端加密與媒體處理——提及它們,並明確說明是否納入範圍。
2. 估算規模
確定驅動設計的數字。假設約20億使用者,每天產生數百億則訊息;在高峰時相當於每秒數十萬則訊息,以及數億台同時連線的裝置——連線管理本身就成了一個重要課題。
3. 連線與傳遞路徑
訊息傳遞需要持久連線,而不是請求/回應式的輪詢。裝置會保持一條到閘道伺服器的WebSocket連線。必須有一個中心元件知道每位使用者的連線目前由哪個閘道持有,這樣訊息才能被路由到正確的伺服器。
- WebSocket閘道:維持裝置的活躍連線,處理發送/接收。
- 工作階段/狀態登錄:將使用者對應到目前的閘道伺服器(例如存在Redis中),於連線/斷線時更新。
- 訊息路由:發送方的閘道查找接收方的閘道並轉發;若對方離線,訊息會被加入佇列。
4. 訊息儲存與離線傳遞
訊息必須在接收方斷線的情況下依然保留。將每則訊息持久化保存,並按接收方追蹤傳遞狀態;若接收方在線就立即傳遞,否則先儲存,等其重新連線後再轉發(store-and-forward)。
以對話為鍵、寫入密集的存取模式,更適合使用類似Cassandra這樣的寬列儲存(高寫入吞吐量、水平擴展),而不是單一的關聯式資料庫。一旦某則訊息被確認傳遞給所有接收方,就可以將其刪除或讓其過期,因為WhatsApp不會在伺服器端無限期保留已傳遞的訊息。
5. 群組聊天
群組訊息本質上是一種扇出:一次發送變成N次傳遞。對於常見的群組規模,寫時扇出(fan-out-on-write,即推送到每個成員的傳遞路徑)實作簡單、讀取迅速。要注意其取捨——非常大的群組會讓寫時扇出的成本變得很高,這也是為什麼超大型廣播群組是一個不同的設計問題。
6. 需要說出來的取捨
最後點出這些張力:順序性(按對話的序號與全域順序之間的取捨)、回條的一致性與可用性(「最後上線時間」用最終一致性即可,送達確認則需要更強的一致性),以及群組場景下寫時扇出與讀時扇出的取捨。明確說出這些取捨,才能讓答案顯得老練。
藉助即時輔助練習WhatsApp的系統設計
Natively的系統設計面試助手能在實際面試中幫你保持結構清晰——即時建議接下來該涵蓋的面向,並呈現取捨,一切都在你自己的裝置上完成。
