WebSocket負(fù)載均衡實(shí)現(xiàn)
優(yōu)化資源使用、最大化吞吐量、最小化響應(yīng)時(shí)間并避免任何單個(gè)資源的過載.
WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通訊的協(xié)議。它允許服務(wù)器主動(dòng)向客戶端推送信息,客戶端和服務(wù)器之間的連接持久化,使得雙方可以實(shí)現(xiàn)實(shí)時(shí)的數(shù)據(jù)傳輸。WebSocket廣泛應(yīng)用于需要實(shí)時(shí)通信的應(yīng)用場(chǎng)景,如在線聊天、實(shí)時(shí)通知、游戲等。
負(fù)載均衡技術(shù)可以將客戶端的請(qǐng)求分發(fā)到多個(gè)服務(wù)器上,從而平衡服務(wù)器的負(fù)載,提高系統(tǒng)的整體性能和可靠性。常見的WebSocket負(fù)載均衡算法包括:
輪詢(Round Robin):按順序?qū)⒄?qǐng)求分配給各個(gè)服務(wù)器,確保每臺(tái)服務(wù)器都能處理一定數(shù)量的請(qǐng)求。
加權(quán)輪詢(Weighted Round Robin):在輪詢的基礎(chǔ)上,根據(jù)服務(wù)器的性能或負(fù)載情況設(shè)置權(quán)重,將請(qǐng)求優(yōu)先分配給性能較好或負(fù)載較低的服務(wù)器。
最小連接數(shù)(Least Connections):將請(qǐng)求分配給當(dāng)前連接數(shù)最少的服務(wù)器,以確保所有服務(wù)器上的連接數(shù)盡可能平衡。
哈希算法(Hashing):根據(jù)請(qǐng)求的特定屬性(如IP地址、用戶ID等)計(jì)算哈希值,將相同哈希值的請(qǐng)求分配給同一臺(tái)服務(wù)器,以實(shí)現(xiàn)請(qǐng)求的精準(zhǔn)分發(fā)。
在管理大型系統(tǒng)時(shí),負(fù)載均衡問題一直是一個(gè)熱門問題。負(fù)載均衡旨在優(yōu)化資源使用、最大化吞吐量、最小化響應(yīng)時(shí)間并避免任何單個(gè)資源的過載,因此解決此問題對(duì)于性能至關(guān)重要。在本文中,我們將了解該問題的可能解決方案。
為了更好地理解 WS 負(fù)載平衡,讓我們更深入地了解 TCP 套接字的背景。默認(rèn)情況下,單個(gè)服務(wù)器可以處理 65,536 個(gè)套接字連接,因?yàn)樗强捎?TCP 端口的最大數(shù)量。因此,由于 WS 連接具有 TCP 性質(zhì),并且每個(gè) WS 客戶端都占用一個(gè)端口,因此我們可以肯定地說 websocket 連接的數(shù)量也是有限的。
實(shí)際上,這只是半真半假。服務(wù)器可以處理每個(gè) IP 地址的 65,536 個(gè)套接字。因此,可以通過向服務(wù)器添加額外的網(wǎng)絡(luò)接口來輕松擴(kuò)展數(shù)量。同時(shí),跟蹤服務(wù)器上存在的連接數(shù)量非常重要。
一旦超過限制,其他 TCP 連接可能會(huì)遇到很多問題(例如,無法通過 ssh 連接到服務(wù)器)。因此,在應(yīng)用程序代碼中限制每個(gè)節(jié)點(diǎn)的 WS 連接是一個(gè)好主意。當(dāng)我們處理 websockets 時(shí),我們?cè)趹?yīng)用程序中做同樣的事情。
一旦我們了解了主要限制和克服它的方法,我們就可以繼續(xù)進(jìn)行負(fù)載均衡。下面我將描述我們?cè)谄渲幸粋€(gè)項(xiàng)目中嘗試的 3 種方法。請(qǐng)注意,所有系統(tǒng)部件都已部署到 AWS,一些提示和提示僅適用于 Amazon 配置。設(shè)計(jì)WebSocket負(fù)載均衡方案時(shí),可以考慮以下幾個(gè)方面:
選擇合適的負(fù)載均衡器:根據(jù)實(shí)際需求選擇合適的負(fù)載均衡器,如Nginx、HAProxy等,它們支持WebSocket協(xié)議并提供豐富的負(fù)載均衡策略。
配置粘性會(huì)話:在負(fù)載均衡器中配置粘性會(huì)話,以確保同一客戶端的WebSocket連接在多次請(qǐng)求中始終被分發(fā)到同一臺(tái)服務(wù)器上。
監(jiān)控和日志:配置監(jiān)控和日志系統(tǒng),以便實(shí)時(shí)監(jiān)控服務(wù)器的負(fù)載情況和WebSocket連接的狀態(tài),及時(shí)發(fā)現(xiàn)并解決問題。
故障恢復(fù)機(jī)制:設(shè)計(jì)故障恢復(fù)機(jī)制,以便在服務(wù)器發(fā)生故障時(shí)能夠自動(dòng)將連接和消息流量轉(zhuǎn)移到其他服務(wù)器上,保證系統(tǒng)的持續(xù)運(yùn)行。
ELB方案
實(shí)施負(fù)載均衡的最簡(jiǎn)單方法是使用 AWS 提供的 Elastic Load Balancer??梢詫?ELB 切換到 TCP 模式,從而實(shí)現(xiàn)任何類型的 TCP 連接的負(fù)載均衡,包括 websockets。這種方法提供:
LB 的自動(dòng)故障轉(zhuǎn)移
負(fù)載均衡節(jié)點(diǎn)的自動(dòng)擴(kuò)展
設(shè)置非常簡(jiǎn)單
基本上,對(duì)于大多數(shù)常見情況,這是一個(gè)很好的解決方案,直到您的負(fù)載出現(xiàn)飛濺增長(zhǎng)。在這種情況下,ELB 變得太慢,無法建立新連接。可以聯(lián)系 Amazon 并要求他們“預(yù)熱”ELB,但當(dāng)我們需要快速建立數(shù)千個(gè) WS 連接時(shí),出于負(fù)載測(cè)試目的,而對(duì)于我們的客戶來說,由于系統(tǒng)的可用性,這對(duì)我們來說不是一個(gè)選擇。
軟件負(fù)載均衡器
我們已經(jīng)嘗試將 HAProxy 作為負(fù)載均衡器。但是要使 HAProxy 正常工作,應(yīng)該記住我們上面討論的端口限制問題。要使 HAProxy 處理超過 65k 個(gè)連接,我們應(yīng)該完成以下步驟:
1. 創(chuàng)建一堆私有 IP 地址。為此,請(qǐng)選擇您的 Amazon 實(shí)例 -> 操作 -> 聯(lián)網(wǎng) -> 管理私有 IP 地址。即添加了 3 個(gè) IP 地址:192.168.1.1、192.168.1.2、192.168.1.3。請(qǐng)記住,IP 應(yīng)該與你的真實(shí)應(yīng)用程序服務(wù)器位于同一子網(wǎng)中;
2. 通過 SSH 連接到您的 HAProxy 實(shí)例并運(yùn)行以下命令:
$> ifconfig eth0:1 192.168.1.1
$> ifconfig eth0:2 192.168.1.2
$> ifconfig eth0:3 192.168.1.3
這將向?qū)嵗砑?3 個(gè)虛擬網(wǎng)絡(luò)接口;
3. 配置 HAProxy。以下是文件中接受 WS 連接的 3 個(gè) Erlang 節(jié)點(diǎn)的部分。haproxy.cfg
listen erlang_front :8888
mode http
balance roundrobin
timeout connect 1s
timeout queue 5s
timeout server 3600s
option httpclose
option forwardfor
server erlang-1 192.168.0.1:8888 source 192.168.1.1
server erlang-2 192.168.0.2:8888 source 192.168.1.2
server erlang-3 192.168.0.3:8888 source 192.168.1.3
現(xiàn)在 HAProxy 可以處理超過 65,536 個(gè) websocket 連接,并且可以通過添加虛擬網(wǎng)絡(luò)接口輕松增加連接限制。此外,它可以相當(dāng)快速地建立新的連接。
盡管存在以下缺點(diǎn),但這種方法似乎是可行的:
故障轉(zhuǎn)移 HAProxy 實(shí)例應(yīng)使用以下工具手動(dòng)設(shè)置keepalived;
每當(dāng)添加新的 Erlang 節(jié)點(diǎn)時(shí),都必須做一些事情來重新配置 HAProxy;
隨著連接數(shù)量的增加,沒有選項(xiàng)可以水平擴(kuò)展 HAProxy。我們只有垂直選項(xiàng)可用,因此當(dāng)我們有越來越多的活躍用戶時(shí),我們應(yīng)該為 HAProxy(和 HAProxy 鏡像節(jié)點(diǎn))獲得越來越昂貴的實(shí)例。
我們對(duì)這些缺點(diǎn)沒問題,但實(shí)施了更簡(jiǎn)單的解決方案。這是可能的,因?yàn)槲覀円呀?jīng)實(shí)現(xiàn)了一些代碼,并且我們的系統(tǒng)設(shè)計(jì)允許我們使用自定義方法。
自定義方案
為了繼續(xù)前進(jìn),讓我們回顧一下以下顯示系統(tǒng)架構(gòu)的圖表。

我們有一個(gè) JavaScript 客戶端應(yīng)用程序、一個(gè)負(fù)責(zé)用戶授權(quán)的身份驗(yàn)證應(yīng)用程序和一個(gè)具有主要應(yīng)用程序功能的 Erlang 應(yīng)用程序。流程如下:
客戶端使用憑證向 Auth Application 發(fā)出 HTTP 請(qǐng)求;
Auth Application 檢查 creds,生成 token 并通過 HTTP 請(qǐng)求發(fā)送到 Erlang Cluster;
Erlang 應(yīng)用程序確認(rèn)收到令牌并向 Auth 應(yīng)用程序發(fā)送帶有確認(rèn)的 HTTP 響應(yīng);
Auth App 向客戶端應(yīng)用程序發(fā)送 HTTP 響應(yīng)。此響應(yīng)包括生成的令牌;
客戶端使用令牌通過我們的 HAProxy 負(fù)載均衡器與 Erlang 應(yīng)用程序建立 websocket 連接。
這是我們的基本流程,稍作修改。我們?cè)?Erlang 應(yīng)用程序中添加了一個(gè)簡(jiǎn)單的模塊,用于跟蹤每個(gè) Erlang 節(jié)點(diǎn)上的 websocket 連接數(shù)量。由于 Erlang 的 “分布式” 特性,每個(gè)節(jié)點(diǎn)都知道其他節(jié)點(diǎn)的連接。
所以我們可以選擇一個(gè)連接較少的節(jié)點(diǎn)。我們獲取此節(jié)點(diǎn)的公網(wǎng) IP 地址,然后發(fā)送回 auth 應(yīng)用程序。然后,身份驗(yàn)證應(yīng)用程序?qū)⒋?IP 與令牌一起發(fā)送回客戶端??蛻舳耸褂檬盏降?IP 地址和令牌建立 WS 連接。所以最終的圖表是這樣的:

現(xiàn)在我們可以:
擺脫 WS 負(fù)載均衡器,這使我們的系統(tǒng)不那么復(fù)雜;
添加 Erlang 節(jié)點(diǎn),而無需重新配置系統(tǒng)的其他部分。
另外:
WS 連接現(xiàn)在在 Erlang 節(jié)點(diǎn)之間均勻分布;
該系統(tǒng)可輕松水平擴(kuò)展;
我們不必對(duì) Erlang 節(jié)點(diǎn)使用彈性 IP。
綜上所述,WebSocket負(fù)載均衡是一項(xiàng)復(fù)雜但重要的技術(shù),它能夠提高系統(tǒng)的可用性、可擴(kuò)展性和性能。在設(shè)計(jì)和實(shí)現(xiàn)WebSocket負(fù)載均衡方案時(shí),需要充分考慮WebSocket的特殊需求,并選擇合適的負(fù)載均衡器和策略進(jìn)行配置和優(yōu)化。
