Facebook 這類網站如何處理數十億請求並保持高可用性的?
Facebook 這類網站如何處理數十億請求並保持高可用性呢,答案是負載均衡,本文將對其一探究竟。
什麼是負載均衡
負載均衡是許多協同工作資源(通常是計算機)的分配策略。它們通常用於提高容量和可靠性。
為了便於討論負載均衡,對於服務擴充套件我假設以下兩點:
- 我可以執行任意數量例項
- 任何請求可以到達任意例項
第一個假設表明服務是無狀態的(或者像 Redis 叢集一樣可以共享狀態)。第二個假設實際中並不是必須的(比如粘性負載均衡),但在這片文章中做這樣的假設便於討論。
下面是我將要討論的負載均衡技術:
- 應用層(OSI 第七層)負載均衡(HTTP、HTTPS、WS)
- 傳輸層(OSI 第四層)負載均衡(TCP、UDP)
- 網路層(OSI 第三層)負載均衡
- DNS 負載均衡
- 多個子域手動負載均衡
- 任播(Anycast)
最後還有一些其他知識點:
- 延遲和吞吐量
- 伺服器直接返回
這些技術大致按照「網站流量增大時需要的操作步驟」排序。比如,應用層的負載均衡是首先要做的事(遠遠早於任播)。前三條技術提高吞吐量和可用性,但存在單點故障。其餘的三條技術在提高吞吐量的同時避免了單點故障。
為了幫助我們理解負載均衡,我們來看一個簡單的服務擴充套件。
注意:每種擴充套件技術都採用了非技術的比喻(在商店購物)。這些比喻僅僅是描述技術背後的思想,並不完全準確。
服務
我們假設我們正在構建大規模的服務。就像下圖所示:
這個系統並不能處理大量通訊,並且如果它宕機,整個應用就停了。
比喻:
- 你走向商店唯一一條結賬的隊伍。
- 你購買你的商品,如果那裡沒有收銀員,你無法完成購買。
應用層(OSI 第七層)負載均衡
為了承載更大的通訊量,首先用到的就是應用層負載均衡。應用層是 OSI 第七層。它包括 HTTP、HTTPS 和 WebSockets。一款非常流行又久經考驗的應用層負載均衡器就是 Nginx。讓我們看看它如何幫助我們擴充套件服務:
請注意,通過這種技術,我們能負載均衡數十或上百伺服器例項。上面圖片只展示兩個作為例子。
比喻
- 商店員工引領你到一個特別的(有收銀員的)結賬隊伍
- 你購買你的商品
工具:
- Nginx
- HAProxy
注意:
- 我們要在這裡停止使用 SSL(分發時不用 SSL)
傳輸層(OSI 第四層)負載均衡(TCP、UDP)
上一條技術幫助我們承載大量通訊,但如果我們需要承載更大的通訊量,傳輸層負載均衡非常有用。傳輸層是 OSI 第四層,包括 TCP 和 UDP。流行的傳輸層負載均衡器有 HAProxy(這個也用於應用層負載均衡)和 IPVS。讓我們看看它們如何幫助我們擴充套件服務:
應用層負載均衡+傳輸層負載均衡能處理大多數情況下的通訊量。然而我們我們仍要擔心可用性。單點故障有可能出現在傳輸層的負載均衡器。我們在下面一節的 DNS 負載均衡解決這個問題。
比喻:
- 根據客戶會員卡卡號有不同結賬區域。舉個例子,如果你的會員卡卡號是偶數,去電器區附近的結賬臺,否則就去食品區附近的結賬臺。
- 一旦你到達正確的結賬區域,商店員工引領你到一個特別的結賬隊伍
- 你購買你的商品
工具:
- HAProxy
- IPVS
網路層(OSI 第三層)負載均衡
如果我們要繼續擴充套件,我們需要增加網路層負載均衡。這比上面兩條技術更復雜。網路層是 OSI 第三層,包括 IPv4 和 IPv6。下面是網路層負載均衡:
為了搞清楚它如何工作,我們需要一點等價路由的知識(ECMP)。當有多條等價鏈路到達相同地址時,我們使用等價路由。簡單來說,它允許路由器或交換機通過不同連結傳送資料包(支援高吞吐量),最終到達同一地址。
我們可以利用這一點來實現網路層負載均衡,因為在我們看來,每個傳輸層負載均衡器是相同的。這意味著我們可以把從網路層負載均衡器到傳輸層負載均衡器的連結看做相同目的地的鏈路。如果我們把所有負載均衡器繫結到相同 IP 地址,我們可以使用等價路由在傳輸層負載均衡器之間分配通訊。
比喻:
- 街對面有兩家彼此分開卻又一模一樣的商店,你去哪一家完全取決於你的習慣。
- 一旦你到達了商店,根據客戶會員卡卡號有不同結賬區域。舉個例子,如果你的會員卡卡號是偶數,去電器區附近的結賬臺,否則就去食品區附近的結賬臺。
- 一旦你到達正確的結賬區域,商店員工引領你到一個特別的結賬隊伍
- 你購買你的商品
工具:
- 通常在機櫃裡交換機內部的硬體中處理。
太長不閱:
- 除非你的服務規模相當大或有自己的硬體,否則你不需要它。
DNS 負載均衡
DNS 是將名稱轉換為 IP 地址的系統。舉個例子,它可以把 example.com 轉換為 93.184.216.34 。它當然也可以返回多個 IP 地址,像下面這樣:
如果返回了多個 IP,客戶端通常會使用第一個可用的地址(然而一些應用只看第一個返回的 IP)。
目前有很多 DNS 負載均衡技術,比如 GeoDNS 和輪詢排程(round-robin)。GeoDNS 基於不同請求者而返回不同響應。這讓我們可以將客戶端路由到其最近的伺服器或資料中心。輪詢排程會迴圈所有可用的 IP 地址,對於每個響應會返回不同的 IP。如果多個 IP 可用,這兩種技術僅僅改變響應裡的 IP 順序。
下圖展示 DNS 負載均衡如何工作:
在這個例子中,不同的使用者被路由到不同的服務叢集(隨機或基於地理位置)。
現在這裡不再有單點故障的可能性(假設有多臺 DNS 伺服器)。為了進一步提高可靠性,我們可以在不同資料中心執行多個服務叢集。
比喻:
- 你在網上查詢購物中心,返回的列表把最近的購物中心放在第一個。你檢視通往每個購物中心的路,然後選擇列表中第一個營業的購物中心。
- 街對面有兩家彼此分開卻又一模一樣的商店,你去哪一家完全取決於你的習慣。
- 一旦你到達了商店,根據客戶會員卡卡號有不同結賬區域。舉個例子,如果你的會員卡卡號是偶數,去電器區附近的結賬臺,否則就去食品區附近的結賬臺。
- 一旦你到達正確的結賬區域,商店員工引領你到一個特別的結賬隊伍
- 你購買你的商品
手動負載均衡和路由
如果你的內容在許多資料中心或服務間共享,而我們需要路由到其中特定的一個,那麼這條技術就很有用了。比如 cat.jpg
儲存在倫敦的叢集中,但其他叢集中沒有。相似的,dog.jpg
儲存在紐約的叢集中,其他資料中心或叢集中沒有。舉個例子,這很可能發生在內容剛剛上傳,還未在資料中心之間複製的時候。
然而,使用者獲取內容時不應該等待複製完成。這意味著我們的應用需要臨時把所有 cat.jpg
請求傳送到倫敦,所有 dog.jpg
請求傳送到紐約。所以我們需要用 https://lon-1e.static.example.net/cat.jpg
代替 https://cdn.example.net/cat.jpg
。對 dog.jpg
來說也一樣。
為了實現這一點,我們需要為每個資料中心設定子域(最好細分到每個叢集每臺機器)。除了上面的 DNS 負載均衡,這一點也很有必要。
注意:我們的應用需要保持追蹤內容的位置,以便重寫請求。
比喻:
- 你撥打公司電話詢問哪個購物中心提供貓糧。
- 你檢視列表上的購物中心路線,然後選擇第一個營業的。
- 街對面有兩家彼此分開卻又一模一樣的商店,你去哪一家完全取決於你的習慣。
- 一旦你到達了商店,根據客戶會員卡卡號有不同結賬區域。舉個例子,如果你的會員卡卡號是偶數,去電器區附近的結賬臺,否則就去食品區附近的結賬臺。
- 一旦你到達正確的結賬區域,商店員工引領你到一個特別的結賬隊伍
- 你購買你的商品
任播(Anycast)
這篇文章討論的最後一種技術就是任播。首先來看一點背景知識:
大多數網路使用單播。這本質上意味著每臺計算機擁有獨一無二的 IP 地址。有另一種稱為任播的理論。通過任播,一些機器可以使用相同的 IP 地址和路由,並把請求傳送到最近的一臺機器。我們可以把這種技術和上面所講的技術結合起來,構建出高可靠性和可用性,能承載巨大通訊量的系統。
任播根本上來說是允許網際網路為我們處理部分負載均衡。
比喻:
- 你告訴別人你打算去商店,他們把你帶到最近的位置。
- 街對面有兩家彼此分開卻又一模一樣的商店,你去哪一家完全取決於你的習慣。
- 一旦你到達了商店,根據客戶會員卡卡號有不同結賬區域。舉個例子,如果你的會員卡卡號是偶數,去電器區附近的結賬臺,否則就去食品區附近的結賬臺。
- 一旦你到達正確的結賬區域,商店員工引領你到一個特別的結賬隊伍
- 你購買你的商品
雜項
延遲和吞吐量
順便一提的是,這些技術也可以提升低延遲服務的吞吐量。增加服務數量而不是讓每個服務處理更多的通訊。這樣我們就可以得到低延遲、高吞吐量的系統。
伺服器直接返回
在傳統負載均衡系統中,請求穿過負載均衡的所有層級,響應也同樣穿過它們。降低負載均衡通訊量的一個優化點就是伺服器直接返回。這意味著服務端的響應不通過負載均衡。如果服務端的響應十分巨大,這點尤其有用。
打賞支援我翻譯更多好文章,謝謝!
打賞譯者
打賞支援我翻譯更多好文章,謝謝!
任選一種支付方式