How to implement a distributed and auto-scalable WebSocket server architecture on Kubernetes一文中雖然解決是WebSocket長連線問題,但可以為其他長連線負載均衡場景提供參考價值
WebRTC 是一套開放web標準,用於在使用者端之間建立(端到端方式的)直接通訊。WebRTC signaling 是WebRTC協定的前置步驟,它依賴signaling server在需要建立WebRTC連線的使用者端之間轉發協商協定。使用者端和signaling server之間的連線通常使用WebSockets。
這種方式可以解決分散式約束問題,但有兩個關鍵限制:
大部分預設的負載均衡演演算法為round-robin,但這種方式適用於HTTP短連線,不能在自動擴縮容情況下均衡WebSocket連線。另外有一種least-connected演演算法,可以將WebSocket連線請求分配給具有最少active連線的範例。這種方式可以保證在擴容情況下達到最終均衡。
這種方案的問題是並不是所有的負載均衡器都支援least-connected負載均衡演演算法,如Nginx支援,但 GCP’s HTTP(S) 負載均衡器不支援,這種情況下可能要訴諸於比較笨拙的辦法,如readiness probes:即讓具有最多負載的signaling範例暫時處於Unready狀態(此時endpoint controller會從所有service上移除該pod),以此來阻止負載均衡器向該範例傳送新的連線請求。
基於雜湊的負載均衡演演算法是一種確定均衡流量的方法,根據使用者端請求中的內容(如header的值、請求或路徑引數以及使用者端IP等)來計算雜湊值。有兩種著名的雜湊演演算法: 一致性雜湊 和 rendezvous 雜湊。這裡我們選擇了後者,原因是它更加簡單,且均衡性更好。演演算法如下:
H(val, I) = I_i
- H is the hash-based algorithm
- val is the value (extracted from the request) from which the hash is computed
- I = {I_1, I_2, ..., I_N} is the set of all backend instances
- I_i is the backend instance that was "selected" by the algorithm
如果使用使用者端的clientId
作為引數val
,那麼就可以將每個使用者端對映到特定的signaling範例上。此外,只要知道clientId
和後端範例,就可以通過該函數了解到使用者端和範例的對應關係,這也意味著,如果一個signaling範例接收到發起端的訊息,但沒有在本地找到接收端,此時就可以通過雜湊演演算法知道接收端位於哪個範例上。下面看下具體實施步驟:
clientId
作為rendezvous 雜湊的入參。clientId
,然後從本地查詢接收端,如果找到,則通過WebSocket將訊息轉發給對端即可,如果沒有找到,則使用rendezvous 雜湊演演算法,並使用clientId
作為val
,signaling範例的IPs作為I
,計算出接收端註冊的範例I₂。如果 I₂ = I₁ ,說明接收端已經斷開連線或從未註冊,反之則直接將訊息轉發給 I₂ 。使用基於雜湊的負載均衡可以優雅地解決分佈性約束,通過kubernetes Endpoint API也可以很容易地獲取signaling範例的變動。rendezvous雜湊的一個特點是,當新增或刪除後端範例時,會改變函數的引數I
,函數的返回值只會影響一部分資料(如果範例從N-1擴充套件為N,則平均影響1/N的資料)。
但在範例變更之後,誰去負責重新分配註冊的使用者端?下面有兩種方式解決該問題:
當一個signaling範例Iᵢ通過kubernetes Engpoint API探測到擴縮容事件後,它會遍歷本地註冊的所有使用者端,然後使用rendezvous雜湊演演算法針對更新後的範例集中的每個clientId重新計算所有結果。理論上,計算出的部分新結果不屬於Iᵢ,此時Iᵢ可以斷開這部分使用者端的WebSocket連線,如果使用者端有重連機制,就會重新發起建鏈,當請求到達負載均衡器之後,會被分配到正確的signaling範例上。
擴容前
在擴容後,觸發使用者端重連
該方式比較簡單,但存在一些弊端:
出於上述原因,我們放棄了這種方式。
這裡我們自己實現了負載均衡器,但僅用於代理WebSocket的請求和訊息,不處理如TLS和ALPN之類的功能(這部分由前置的負載均衡處理)。實現步驟如下:
clientId
,然後使用rendezvous雜湊演演算法並代入新的後端範例重新計算結果。當返回的範例與當前使用者端註冊的不一致,則負載均衡器只會斷開與該使用者端相關的 負載均衡器-signaling 之間的WebSocket,並重新建立一條到正確的signaling範例的 負載均衡器-signaling 連結。文中最後使用自實現的負載均衡器來緩解後端範例擴縮容對使用者端的影響。需要注意的是,rendezvous雜湊演演算法在擴容場景下不大友好,需要重新計算所有key(文中為clientId
)的雜湊值,因此在資料量大的情況下會造成一定的效能問題,因此適合資料量減小或快取場景。
本文來自部落格園,作者:charlieroro,轉載請註明原文連結:https://www.cnblogs.com/charlieroro/p/17684024.html