關於負載平衡和分片 - Tim Bray

banq發表於2019-09-28

如果您確實需要處理大量流量,則只有一種方法:分片。也就是說,根據需要將傳入請求分配給儘可能多的主機(或Lambda函式,訊息代理或資料流)。一旦完成這項工作,您就可以處理幾乎無限的請求量。當然,您必須選擇如何在分片之間分配流量。自從我開始在AWS工作以來,我就對這些選項非常關注。

隨機傳送
這是可以想象的最簡單的方法。對於每條訊息,您都將建立一個UUID並將其用作分割槽金鑰(如果下游使用該UUID進行分片),否則只需使用您喜歡的隨機數生成器來選擇目標分片即可。
假設前端可能是愚蠢的,無狀態的且快速的。負載將在後端分片之間平均分配。
關於此主題的常見變體涉及自動縮放。例如,如果您有一群主機在處理來自SQS佇列的訊息,則基於佇列深度自動縮放佇列大小是一種很常見的做法。再一次,令人讚歎的簡單。

“智慧”分片 
如果某些分片超載而其他分片處於閒置狀態。另一類情況是上游傳送“毒丸”訊息,這些訊息導致接收碎片鎖定或行為異常。
負載敏感度是一種“智慧”方法。這樣做的目的是跟蹤每個分片上的負載,並有選擇地將流量路由到負載較輕的負載,並遠離繁忙的負載。最簡單的事情是,如果您有某種負載度量標準,請始終選擇具有最低流量的分片。
但這不是那麼容易。您必須找出有意義的負載指標(佇列深度?最近收到了多少流量?CPU負載?),並將其從每個分片傳送到前端。
如果您擔心使用藥丸-一點也不罕見-最明智的方法通常是洗牌。請參閱 ColmMacCárthaigh關於該主題的精彩文章

相似性
也稱為“會話相似性”。您有時聽到的另一個術語是“粘性會話”;有關討論,請參閱 Red Hat上的這篇文章。這個想法是透過使用某種形式的帳號或會話識別符號作為分割槽鍵,將所有事件從同一上游源路由到同一分片。
如果來自同一使用者的所有點選流點選或來自同一工作流或進入同一主機的任何狀態更改事件,您都可以將相關狀態保留在該主機的記憶體中,這意味著您可以更快地做出響應並達到目標資料庫不太難。
很多事情都會出錯。最明顯的是:流量在上游源之間分佈不均。
一次我正在處理一系列AWS客戶事件,我認為按帳號將其分配給Kinesis分片是明智的。太糟糕了 -  每個帳戶速率每秒的訊息以經典的平方反比曲線分佈在各個帳戶中,就像邊距一樣。具體來說,在一項早期測試中,我注意到排名前10位的帳戶佔了流量的一半。
一般的教訓是,如果不幸地將太多“熱”上游源傳送到同一分片,則很容易使它過載。最糟糕的情況是,當您面臨單個“鯨魚一樣大型”客戶端時,對於任何單個分片來說,其流量都過多。
所以在這種情況下,我們切換到隨機傳送,整個系統穩定下來並執行順暢。除此以外,處理每條訊息都需要諮詢以帳號為鍵的資料庫,而資料庫的費用卻很高。
因此,我想到了一個絕妙的主意,編寫了一個“盡力而為的親和力”庫,該庫試圖將每個客戶的請求聚集在儘可能少的分片上。它似乎工作正常,我們的資料庫賬單減少了六分之一,我感到很聰明。
從那時起,這變成了一場噩夢。遭遇我們從未想到的極端情況。
在某個主機中建立了每個會話狀態,然後該主機崩潰時,會發生什麼呢?(不一定是當機,也許您只需要打補丁即可。
現在,因為您是一位優秀的設計師,所以您一直在將所有更新內容都寫入某種日誌中,因此,當分片變成梨形時,您會為其找到另一個主機並重播事件日誌。
為此,您需要:

  1. 可靠地檢測節點何時發生故障。而不是停留在較長的GC週期中或等待緩慢的依賴關係。
  2. 查詢新主機以放置分片。
  3. 找到正確的日誌並重播以重建所有丟失的狀態。

任何人都可以看到這需要很多工作,程式碼根本不簡單。

將狀態儲存在碎片中是不行的,我們都應該回到無狀態隨機傳送狀態,那延遲呢?好吧,如果您使用分割槽鍵進行分片,則可能可以使用它從鍵/值儲存中檢索狀態,例如DynamoDB或Cassandra或Mongo。如果一切設定正確,您可以期望穩態檢索延遲(單位毫秒)。您可能可以使用DynamoDB的DAX之類的快取加速器, 並且做得更好。
但是快取是一種干擾。您將要獲得的效能將取決於您的記錄大小和更新方式,並且無論如何您都不關心P99的平均值或中位數 。
也就是說,執行一些測試。您可能會發現從資料庫中獲得了足夠的效能,可以在各個分片之間進行隨機傳送,擁有無狀態的前端,並在晚上自動縮放後端。

相關文章