WebSocket 擴展:垂直 vs 水平、load balancer 與 backplane
文字解析 · 1 支影片 · 產生於 2026-10-01 00:54
章節(8)
1. Outline
- 起點 · How to scale WebSockets to millions of connections
從「WebSocket 為何難擴展」出發,先拆掉 benchmark 迷思,再比較垂直與水平擴展,最後用 Redis backplane 解決多台伺服器狀態分裂,並列出水平擴展自己的挑戰。
2. YouTuber 的思維推導
How to scale WebSockets to millions of connections
Ably Realtime · 14m00s · 字幕 en · vision=on (input 指定 vision=true。影片是動畫講解型,架構圖(load balancer、server 1/2、Redis backplane)與清單畫面是理解水平擴展問題的關鍵。) · YouTube
預估 vs 實際耗時
| 階段 | 預估 | 實際 |
|---|---|---|
| fetch | 6s | 2s |
| segment | 58s | 2m30s |
| shot | 39s | 9s |
| analyze | 6m15s | 7m20s |
| render | 5s | – |
作者(Ably 的 Alex)從一個觀察出發:很多人在問「WebSocket 要怎麼擴展」,而這個問題之所以難,是因為 WebSocket 是有狀態、長時間存活的連線,伺服器得為每條連線持續綁定 memory 與 CPU,跟 HTTP 短連線、無狀態的性質正好相反,連線一多就會撞硬體上限,而且往往剛好發生在產品起飛時。他先清掉兩個前提:網路上「原生 WebSocket 撐百萬連線」的 benchmark 不可信,因為上線要多做 heartbeat、buffering 等工作;而且還得準備 HTTP long polling 這類 fallback,它的擴展方式又完全不同。接著他把後端擴展拆成兩條路——垂直(加大一台)與水平(加多台 + load balancer)——並先檢驗最誘人的那條:如果「一台能撐多少」的數字夠大,垂直擴展就最省事。
但他的結論是這個數字沒有答案,而且就算夠大,垂直擴展仍有四個實務問題:貴、終究撞資源限制、單點故障讓部署變得綁手綁腳、程式錯誤或雲端區域故障會讓整個服務下線。既然單機的根本問題是「只有一個故障點」,他轉向水平擴展:多台機器可動態增減、一台掛了別台接手,load balancer 用 round robin 之類的演算法分流。但他隨即用一張圖揭露水平擴展的代價:連線被分到不同伺服器後,狀態就碎了——server 2 不知道 server 1 上發生什麼,聊天訊息送不到、廣播也拿不到完整連線清單。
解法是把連線狀態放到 process 之外,用 Redis pub/sub 這類 message broker 當 backplane,讓所有伺服器共享同步的狀態視圖。然後他誠實地列出水平擴展自己的挑戰:資料與連線狀態的同步、Redis 變成新的單點故障要複製、單台逼近上限時要做 load shedding、以及被踢或掛機後大量連線同時湧回的 thundering herd。最後回到起點的問題:「一台能撐多少」本身就問錯了,因為連線各不相同,而且單機終究是單點故障;水平擴展是可靠、高擴展性後端的關鍵,但複雜度要自己承擔。
他不下定論——能接受停機風險就一台垂直擴展的伺服器也行,金融這類不能停的服務則值得一開始就投資水平擴展。
推理鏈:每段留給下一段的線索
每一列是一段,箭頭後的字是這段留給下一段的線索。點段落標題跳到該段。
- 為什麼 WebSocket 難以擴展 → 作者說「我們先看擴展 WebSocket 的挑戰」——但在講怎麼擴展之前,他要先處理一個常見的錯誤參照:網路上那些「原生 WebSocket 撐百萬連線」的 benchmark,為什麼不能拿來當你的容量規劃依據?
- 為什麼 WebSocket benchmark 會誤導 → 把通用成本清掉之後,作者說「來看你擴展 WebSocket 的選項」——後端擴展到底有哪幾條路?而其中最直覺的那條(把一台機器加大)能不能直接解決問題?
- 兩種擴展方式與「一台能撐多少」 → 作者話鋒一轉:「就算那個數字真的很高」,垂直擴展還有一些實務上的考量要知道——他整理了一份小清單,那些問題是什麼?
- 垂直擴展的實務問題 → 單機的根本問題是「只有一個故障點」——作者說「也許水平擴展能解決其中一些問題,來仔細看看」:多台機器怎麼分流?分流之後又是誰在指揮?
- 水平擴展與 load balancer → 作者說「不幸的是,沒那麼簡單」、「水平擴展最大的缺點是它引入的架構複雜度」——具體是什麼複雜度?他準備了一張圖來說明。
- 狀態分裂與 backplane → 作者反問:「所以水平擴展是魔法銀彈嗎?完全不是」——加了 backplane 之後,水平擴展自己又帶來哪些挑戰?
- 水平擴展的挑戰 → 所有問題都能解、但都要付出複雜度——那到底該不該走水平擴展?作者回到影片一開始那個問題「一台能撐多少連線」,準備給出他的最終看法。
- 結論:依需求選擇
3. 逐段說明
How to scale WebSockets to millions of connections
1. 為什麼 WebSocket 難以擴展 0:00–1:32
作者先點出核心矛盾:WebSocket 是有狀態、長連線的協定,伺服器要為每條連線持續占用 memory 與 CPU,這和 HTTP 短連線、無狀態、容易擴展的性質相反。連線數一多,伺服器就會撞到硬體上限,而且通常剛好發生在產品開始起飛的時候。這支影片不是教學,而是整理擴展到數千到數百萬連線的通用做法與模式。

推理因為作者觀察到很多人在問「WebSocket 怎麼擴展」,所以他先回答「為什麼這個問題值得問」:WebSocket 跑在一條 stateful、長時間存活的連線上,伺服器要為它持續綁定 memory 和 CPU,期限不定;HTTP 則是 stateless、連線短暫,短期對伺服器負擔輕、長期也好擴展。所以只要連線數多(或預期會多),單台伺服器就會撞到硬體上限,出現效能與穩定性問題——而這通常剛好發生在產品開始有人用的時候。作者由此定下影片的範圍:不是逐步教學、不綁特定技術,而是整理把 WebSocket 擴展到數千、數萬、數十萬甚至百萬連線的通用做法與模式。
- CWebSocket 是 stateful 長連線:伺服器要為每條連線常駐 memory/CPU;HTTP 是 stateless→ 畫地圖
- Rfull-duplex 跟 HTTP 半雙工的差別→ 存+回想
AI 補充截圖(30 秒)左邊是 WebSocket:先一來一回的「Initial HTTP handshake」,之後同一條線上 full-duplex、persistent 地雙向傳訊,直到雙方 Close;右邊是 HTTP request-response cycle:Request 1 / Response 1、Request 2 / Response 2 各自獨立。作者說 WebSocket「stateful」時指的不只是 TCP 連線一直開著,還包括伺服器記憶體裡必須為每條連線保留 socket buffer、這個 client 是誰、訂閱了什麼等資訊——這些是 HTTP 每次請求做完就能丟掉的。他沒明說但推理上需要的一步:正因為狀態綁在「某一台伺服器的記憶體」,後面任何把負載搬到別台的做法都得先處理這些狀態要放哪,這是整支影片真正的主軸。
2. 為什麼 WebSocket benchmark 會誤導 1:32–3:03
網路上常見「原生 WebSocket 撐到百萬連線」的 benchmark,但那不切實際:上線後還要做 heartbeat、buffering、未送達訊息處理等額外工作,讓 WebSocket 比 benchmark 看起來更吃資源。另外還要準備 HTTP long polling 這種 fallback transport,以應付防火牆或錯誤設定的 proxy 擋掉 WebSocket 的情況;long polling 相容性高但效率低,而且擴展方式跟 WebSocket 完全不同,複雜度再加一層。

推理因為上一段確立了 WebSocket 是有狀態的長連線、資源會隨連線數常駐,所以作者接著檢查「一條連線到底占多少」——而網路 benchmark 給的數字太樂觀。原因是 benchmark 跑的是「原生」WebSocket,但要上線就得額外做 heartbeat、buffer 未送達的訊息等處理,這些讓每條連線比 benchmark 更吃資源。第二個常被忽略的成本是 fallback:防火牆或設定錯誤的 proxy 可能擋掉 WebSocket,你得準備 HTTP long polling 這類替代 transport;它相容性極高但效率很差,對伺服器更吃力,而且擴展 long polling 的方式跟擴展 WebSocket 完全不同,複雜度又多一層。作者把這兩點定位為「不管用哪種方法擴展都會遇到」的通用成本,清掉之後才進入擴展選項。
AI 補充截圖(125 秒)標題「WebSockets — Batteries not included!」列出了作者只念了兩項的完整清單:Heartbeat、Buffer undelivered messages、Message routing、Broadcast、Handling backpressure、Automatic reconnection、Message acknowledgements、Encryption、Multiplexing。這九項就是「原生 WebSocket 沒附的電池」,每一項都會在每條連線上加記憶體或 CPU:heartbeat 要為每條連線排 timer;buffer 要為離線的 client 暫存訊息;backpressure 要為慢的 client 保留佇列。所以「百萬連線」的 benchmark 量的其實是空連線的上限,不是有業務邏輯的連線。關於 fallback,作者沒解釋「為什麼擴展方式完全不同」:long polling 是一連串短命的 HTTP 請求,每次請求可能落到不同伺服器,所以它的「一條連線」其實是散在多台機器上的多個請求——這正好是第 1 段說的 stateless 特性,需要用 HTTP 那套(無狀態、靠 session id 找回上下文)來擴展,跟 WebSocket 一條長連線固定在一台伺服器的模型是兩套邏輯。
3. 兩種擴展方式與「一台能撐多少」 3:03–4:14
後端擴展只有兩條路:垂直擴展(scaling up,給同一台機器加 CPU、RAM)與水平擴展(scaling out,加機器並用 load balancer 分流)。作者先看垂直擴展,並提出大家最常問的問題:一台伺服器實際上能撐多少 WebSocket 連線?如果那個數字夠用,就能一直加資源、省掉水平擴展的麻煩。但答案是「沒有答案」,它取決於伺服器資源、WebSocket 實作與你的需求。

推理因為上一段把「每條連線到底多貴」講清楚了,所以作者接著把後端擴展的選項攤開:只有兩種——vertical scaling(scaling up,給同一台加 CPU、RAM)和 horizontal scaling(scaling out,加機器,前面放一個 load balancer 平均分流)。他先看垂直擴展,因為它對應到大家(包括他自己)最先問的問題:「一台伺服器實際上能撐多少 WebSocket 連線?」這個問題之所以重要,是因為如果那個數字夠大、能涵蓋你的使用者與成長,就可以一直加資源、避開水平擴展的麻煩。但作者的回答是「沒有好答案」:它取決於伺服器資源、你的 WebSocket 實作,以及你的具體需求——正好呼應第 2 段說的,每條連線的成本取決於你附了哪些「電池」。
- C垂直擴展:給同一台機器加資源,架構不用改,但只有一台,有物理上限→ 畫地圖
- C水平擴展:加多台機器+load balancer 分流,容量理論上沒上限,但狀態要重新設計→ 畫地圖
- CLoad balancer:站在多台伺服器前分配連線;WebSocket 只在 handshake 分一次,之後固定黏住→ 畫地圖
- R「一台能撐多少」是問錯的問題→ 存+回想
AI 補充截圖(205 秒)這一刻畫面只有作者本人,並沒有出現垂直 vs 水平的示意圖,所以這段的理解要靠文字:垂直=一台越換越大,水平=多台一樣的機器加上分流器。作者說「沒有答案」時可以再具體一點:撐多少連線的瓶頸通常不是 CPU 而是記憶體與 OS 限制——每條連線的 socket buffer 加上應用層狀態,從幾 KB 到幾十 KB 不等,一台 16 GB 的機器空連線可以到數十萬,但一加上第 2 段那九項「電池」、再加上訊息真的在流動,數字可能掉一個數量級。所以「一台能撐多少」得用你自己的實作、真實的訊息流量去壓測,別人的數字沒有參考價值。另外作者這裡口誤把「沒有好答案的問題」說成「there isn't a good question to that answer」,意思是「這個問題沒有好答案」。
4. 垂直擴展的實務問題 4:14–6:30
即使那個數字夠高,垂直擴展仍有四個實務問題:一、貴——得按最高峰值配置資源,但多數時間(例如深夜)用不到;二、終究會撞到某個資源限制(thread 數、ephemeral port),要調 kernel 參數,需要專業知識;三、只有一台就是單點故障,升級、重新部署都得挑離峰時間、儘量少做,跟 continuous deployment 背道而馳;四、memory leak 之類的程式錯誤或雲端供應商的區域故障,都會讓整個服務下線。

推理因為上一段得出「一台能撐多少」沒有答案、且就算有也不能只看數字,所以作者列出垂直擴展的四個問題。一、貴:得按你預估的最高同時連線數配置資源,但多數時間(例如深夜使用者都離線)根本用不到。二、就算配了很強的機器,也會在某處撞到資源限制——例如 thread 數或 ephemeral port——要靠調 kernel 參數克服,這需要專業知識,作者坦言自己不敢在正式環境做。三、也是核心論點:只要正式環境只有一台 WebSocket 伺服器,不管它多強,就只有一個故障點。升級、重新部署這種例行事務都得小心不讓服務中斷,但單台通常做不到,只能挑離峰、少做,正好是 continuous deployment 的反面。四、更可怕的是災難:程式有 memory leak 之類需要重啟的錯誤,或即使程式完美、雲端供應商的 region / availability zone 暫時離線——不管機器配得多好,都會整個下線。作者由此提出「也許水平擴展能解決其中一些問題」。
- R按峰值配置的浪費怎麼解→ 存+回想
- R垂直擴展最先撞到的資源限制→ 存+回想
- C單點故障:只要那一台掛了整個服務就停,錢能買更大機器但買不掉「只有一台」這個結構問題→ 畫地圖
- R單台伺服器是持續部署的反面→ 存+回想
- E垂直擴展的四個實務問題→ 存+演練
AI 補充截圖(257 秒)畫面仍只有作者本人,清單並沒有顯示在畫面上,所以四點要從語音整理。作者沒展開的兩個地方值得補:第一,「按峰值配置」的浪費在垂直擴展下無解,因為單台機器不能半夜縮小、白天變大,而這正是水平擴展可以動態增減機器的動機。第二,「資源限制」對 WebSocket 伺服器來說最先撞到的通常是 file descriptor 上限(每條連線一個 fd,Linux 預設 1024,要調 ulimit 與 fs.file-max)與 conntrack 表,然後才是記憶體。作者提到 ephemeral port 則要看情境:伺服器接受連入時只用一個 listening port,不消耗 ephemeral port;真正會耗盡的是 load balancer 或反向 proxy 對後端建立連線的那一側(每個來源 IP 對每個目標只有約 6 萬個 port 可用)。這段最重要的推理是「單點故障」:它把前面「貴」「撞上限」這些可用錢解決的問題,升級成用錢也解不了的結構問題,這就是為什麼作者接下來要轉向水平擴展。
術語:provisioningkernel parameters
「maybe you hit the limit of available threads or ephemeral」
5. 水平擴展與 load balancer 6:30–7:40
水平擴展把負載分散到多台機器,可以隨需求動態增減伺服器(忙時加、離峰減以省錢),一台掛了其他機器能接手。關鍵元件是 load balancer:它站在機器陣列前面,把進來的 WebSocket 連線平均分配出去。分配演算法有好幾種,預設常見的是 round robin(server 1、server 2、server 1、server 2 輪流)。水平擴展是建立可擴展、穩健後端的方法,但不是加幾台機器、打開 load balancer 就完事。

推理因為上一段垂直擴展的四個問題裡,「貴」來自無法隨負載調整、「單點故障」來自只有一台,所以作者接著說明水平擴展正好各個擊破:把負載分散到多台機器後,可以隨需求動態加減伺服器——忙時加、深夜減省錢;一台因任何原因下線也不是世界末日,陣列裡另一台大概能接手。要做到這些,關鍵是 load balancer:從後端架構看它站在機器陣列前面,把進來的 WebSocket 連線平均分配。分配演算法有幾種,畫面上有列,但很多情況預設是 round robin:server 1、server 2、server 1、server 2 輪流。作者總結水平擴展是建立真正可擴展、穩健後端的方法——但馬上補一句:它不是加幾台機器、打開 load balancer 就完事。
AI 補充截圖(436 秒)作者說「這裡畫面上有幾種演算法」,但抽到的這格只有作者本人,沒抓到那張清單,所以補上常見的幾種:round robin(輪流)、least connections(挑目前連線最少的)、IP hash(同一來源 IP 固定到同一台)、weighted 版本(依機器規格給權重)。對 WebSocket 有一個作者沒說、但下一步推理需要的重點:load balancer 只在 handshake 那一刻做分配,之後這條長連線就固定在那台伺服器上,不會再被重新分配。所以 round robin 分的是「新連線」,而不是「訊息」——這表示某個使用者這輩子的所有訊息都只經過他被分到的那一台。另外 round robin 對 WebSocket 不一定平均:連線是長命的,先分到的伺服器上舊連線不會走,加上第 2 段說的每條連線活躍度不同,實際負載可能很不均,least connections 通常更適合長連線。
術語:autoscalingload balancing algorithm
6. 狀態分裂與 backplane 7:40–9:55
水平擴展最大的代價是架構複雜度。作者用示意圖說明:load balancer 把連線分到不同伺服器,但連在 server 1 的客戶端收不到送到 server 2 的訊息。以聊天室為例,user A 在 server 1、user B 在 server 2,兩台之間沒有連結,server 2 完全不知道 server 1 發生什麼,A 的訊息永遠到不了 B;廣播也一樣,連線清單被切碎在兩台上,沒有單一地方可以拿到全部連線。解法是把連線狀態放到 process 之外,用 message broker(常見是 Redis 搭 pub/sub),server 1 透過 Redis 把訊息轉給 server 2 再送給 user B。這叫加一層 backplane,讓所有伺服器共享同步的狀態視圖。



推理因為上一段 load balancer 把連線平均分到不同伺服器(這是分散負載的好方法),所以作者接著指出代價:連在 server 1 的客戶端收不到送往 server 2 的訊息。他用聊天室舉例:load balancer 把 user A 分到 server 1,round robin 輪到 user B 分到 server 2。A 透過 server 1 送訊息給 B,但 B 在 server 2,圖上兩台之間沒有任何連線,server 2 完全不知道 server 1 上發生了什麼——它們成功分散了負載,但從狀態的角度是完全斷開的,A 的訊息根本沒有機會送到 B。第二個例子是廣播:要對所有連線推一則即時更新,最方便的寫法是拿到全部 WebSocket 連線的清單、迴圈逐一送出;但現在連線碎在兩台上,沒有任何一個地方能拿到完整清單。解法是把連線狀態放到 process 之外,交給 message broker——很多開發者選 Redis 搭 pub/sub 模式。回到聊天室:A 透過 server 1 送訊息,server 1 經由 Redis 轉給 server 2,等於在兩台之間建了一條連結,server 2 再透過 B 的 WebSocket 連線送給 B。這有時叫做替基礎設施加一層 backplane,讓各台伺服器對狀態有共享且同步的視圖。
- CPub/sub:發送方發布到頻道,所有訂閱的伺服器都收到,讓伺服器數量跟訊息路由解耦→ 畫地圖
- CBackplane:在多台伺服器背後加共享通道,集中的是訊息與狀態可見性,不是 socket 本身→ 畫地圖
- A沒有 backplane 的兩台伺服器就像互不知道對方的孤島→ 批判類比
- E同一張圖三階段:分散→無連結→加 Redis 後雙向連通→ 存+演練
- Rmessage broker 的角色與 Redis 為何常被選用→ 存+回想
AI 補充三張截圖是同一張圖的三個階段。470 秒:User A(橘線)與 User B(紫線)各自連到 Load balancer,橘線被轉到 Server 1、紫線被轉到 Server 2——顏色清楚表示兩條連線走的是完全不同的路。508 秒:同一張圖,作者這時正說「there's no link between server 1 and server 2」,畫面上 Server 1 與 Server 2 之間確實什麼都沒有。572 秒:右側多了一個 Redis,Server 1 與 Server 2 各有一條雙向的淺藍色線連到它——這條線就是新增的「link」,訊息路徑變成 A → LB → Server 1 → Redis → Server 2 → B。作者跳過了一步:為什麼放到「process 外」就解決了?因為第 1 段講的 stateful 資源(socket 本身)仍然只能留在握有那條 TCP 連線的伺服器上,搬不走;能搬走的是「誰在哪、誰訂閱了什麼、有什麼訊息要送」這層應用狀態。把這層集中到 Redis 之後,任一台伺服器發布訊息,所有訂閱的伺服器都收到,再各自檢查自己手上有沒有目標 client 的 socket。所以 backplane 不是把連線集中,而是把「訊息與狀態的可見性」集中,socket 仍然分散。也要注意 Redis pub/sub 是 fire-and-forget:訂閱者當時不在線就收不到,這跟第 2 段說的 buffering 需求有衝突,需要另外處理(例如 Redis Streams 或有持久化的 broker)。
7. 水平擴展的挑戰 9:55–11:38
水平擴展不是銀彈。主要挑戰是資料同步:除了訊息,還要同步連線狀態(user A 離線時通知 user B)。而且狀態集中在 Redis 後,Redis 變成新的單點故障,得考慮如何複製 broker。另一個負擔是單台伺服器逼近硬體上限時怎麼辦:要偵測快滿了、先拒絕新連線、再主動踢掉部分既有連線(load shedding),讓它們重連到較健康的節點。這又引出恢復連線的問題:不管是被踢還是伺服器掛掉,大量連線會同時湧向同一台伺服器(thundering herd),造成連線錯誤、延遲升高,甚至把那台也拖垮。
推理因為上一段用 Redis backplane 解決了訊息送不到的問題,所以作者接著指出這個解法本身的代價。第一,主要挑戰是資料同步:除了剛講的訊息,還得同步連線狀態——user A 離線時要通知 user B(聊天室顯示對方離線)。第二,既然應用狀態都放進 Redis 了,Redis 就是一個新的單點故障,所以要考慮怎麼複製 Redis 或你用的任何 broker,讓系統在故障時仍然穩健。第三,後端開發者的另一個負擔是「陣列裡某台伺服器逼近硬體上限時該怎麼辦」:要有機制偵測快到上限、先拒絕新連線(不要讓情況更糟),然後大概還得踢掉一部分既有連線(強制斷線),它們會嘗試重連,希望能落到比較健康的節點——作者坦言這個機制實作起來很棘手。第四,它又引出另一個麻煩:恢復連線。不管是你主動把連線從一台踢到另一台,還是某台就是掛了,兩種情況都會有一大批連線同時湧向同一台伺服器,給它不當的壓力,導致連線錯誤、連線延遲、效能下降、延遲上升,甚至那台也跟著下線。作者收尾說這些問題都能解,但都需要一定程度的複雜度。
- Rpresence 與 Redis 自身的複製→ 存+回想
- CLoad shedding:伺服器快到上限時先拒絕新連線、再踢掉部分既有連線,讓它們重連到健康節點→ 畫地圖
- C驚群效應:大量連線同時重連,瞬間壓垮目標伺服器,可能連鎖再掛一台→ 畫地圖
- R指數退避怎麼打散驚群→ 存+回想
- P檢查自己專案的重連邏輯→ 練習
AI 補充這段沒有截圖。作者列的四點其實是一條因果鏈,補上中間的邏輯會更清楚:(1) 訊息要同步 → 用 backplane;(2) 但 backplane 集中了狀態 → 它變成第 4 段說的 single point of failure,於是繞了一圈又回到「單點」問題,只是從伺服器搬到了 Redis;(3) 多台機器各自有上限 → 要能在單台快滿時把負載往外推,這就是 load shedding;(4) 但被推出去的連線會同時重連 → thundering herd。作者說「這些問題都能解」但沒說怎麼解,補幾個標準做法:連線狀態同步通常用 presence 機制(每台伺服器把「我手上有誰」寫進 Redis 並帶 TTL,斷線或機器掛掉時自動過期);Redis 單點用 Sentinel 或 Cluster 做主從複製與自動 failover;thundering herd 的標準解是客戶端重連時用 exponential backoff 加 jitter,把重連時間打散,而不是全部在同一秒衝回來;伺服器端也可以在 load balancer 上做連線速率限制。這裡也解釋了第 5 段預告的問題:autoscaling 縮減機器時不能直接砍,就是因為會製造一次人為的 thundering herd。
8. 結論:依需求選擇 11:38–14:00
回到「一台能撐多少連線」:這個問題本身就問錯了,因為沒有兩條連線是一樣的(活躍程度不同),而且單機就是單點故障,會影響使用者信心甚至業務連續性。水平擴展是建立可靠、高擴展性 WebSocket 後端的關鍵,但要管理 load balancer、多台伺服器和狀態同步。作者不下定論:有些公司確實用一台垂直擴展、程式碼優化良好且容錯的伺服器,接受停機風險以換取簡單;但金融等即時體驗是核心流程、不能接受停機的服務,就值得一開始投資水平擴展。
推理因為前面已經走完垂直擴展的四個問題與水平擴展的四個挑戰,所以作者能對開場的問題下判斷:「一台伺服器能撐多少 WebSocket 連線」根本是個問錯的問題(作者說 mute question,應為 moot question)。理由一,沒有兩條連線是一樣的,有的比別的活躍得多,所以「連線數」本身不是好的容量單位;理由二,只有一台就有單點故障,會讓應用容易停機,影響使用者對服務的信心,甚至影響業務連續性。影片的主結論是:水平擴展是建立可靠、高擴展性 WebSocket 後端的關鍵,但它引入複雜度——你得管理 load balancer、引入多台伺服器,還得想辦法在後端伺服器之間同步狀態。那水平擴展是不是「預設答案」?作者的回答是「也許,只有你知道你在做什麼」:他從研究與跟工程師的對談中得知,確實有些公司就用一台垂直擴展的伺服器,只要程式碼優化得好、有容錯,能撐相當多連線;風險是停機,而在你應用的這個階段,這個風險可能比承擔水平擴展的所有複雜度划算。反過來,金融應用這類即時體驗就是核心流程、完全不能停機的服務,一開始就投資水平擴展是合理的。作者說他不是來告訴你哪個對,只是把選項與挑戰攤開。
AI 補充這段沒有截圖。作者用「連線各不相同」和「單點故障」兩個理由否定「一台能撐多少」這個問題,其實對應影片的兩條線:前者是第 2、3 段講的「每條連線成本取決於實作與活躍度,所以沒有通用數字」;後者是第 4 段的結構性問題。他最後的取捨框架可以整理成一句:用「停機的代價」去換「架構複雜度」——停機代價低(內部工具、早期產品、非核心功能)就先用單台加好的容錯程式碼,停機代價高(金融、交易、即時協作)就一開始做水平擴展。作者沒提但常見的中間路線:先用單台、但一開始就把應用狀態放在 process 外(例如 Redis)而不是記憶體裡,這樣日後加第二台時只需要加 load balancer,不必重寫狀態邏輯;以及乾脆把這整層外包給託管的即時訊息服務——這也是作者所屬公司 Ably 的產品,觀看時可留意這層立場。
4. 總結
WebSocket 是有狀態的長連線,伺服器要為每條連線常駐 memory 與 CPU(第 1 段)→ 所以 benchmark 的「百萬連線」不可信,上線還要 heartbeat、buffering、long polling fallback(第 2 段)→ 擴展只有垂直與水平兩條路,而「一台能撐多少」沒有答案(第 3 段)→ 就算數字夠大,垂直擴展仍貴、會撞資源限制、是單點故障、無法無中斷部署(第 4 段)→ 水平擴展用 load balancer 分流、可動態增減、一台掛了別台接手(第 5 段)→ 但連線分散後狀態就碎了:A 在 server 1 的訊息到不了 server 2 的 B,廣播也拿不到完整清單,解法是把狀態放到 process 外、用 Redis pub/sub 當 backplane(第 6 段)→ backplane 又帶來同步、Redis 單點、load shedding、thundering herd 等挑戰(第 7 段)→ 結論:「一台能撐多少」是問錯的問題,水平擴展是可靠後端的關鍵但複雜度自己扛;能接受停機就一台也行,不能停機的服務值得一開始就投資水平擴展(第 8 段)。
勘誤總整理
確定錯誤/已過時見仁見智(取決於版本或情境)
| 段落 | 原話(transcript 逐字) | 說明 |
|---|---|---|
| 4. 垂直擴展的實務問題 4:49 |
「maybe you hit the limit of available threads or ephemeral」 | WebSocket 伺服器接受連入時只用一個 listening port,本身不會耗盡 ephemeral port;會耗盡的是 load balancer / 反向 proxy 對後端建立連線的那一側(每個來源 IP 對同一目標約 6 萬個 port)。單台伺服器最先撞到的通常是 file descriptor 上限(ulimit -n、fs.file-max)與記憶體。是否適用取決於你的架構裡伺服器前面有沒有 proxy,所以標為見仁見智。字幕的「ephemeral pause」是「ephemeral ports」的辨識誤差。 依據: TCP 四元組定義(RFC 793 / RFC 9293);Linux 預設 ip_local_port_range 32768–60999 |
5. 推薦三個下一步
1. 往下挖深:Redis pub/sub backplane 實作
第 6 段只用一張圖說「server 1 經由 Redis 轉給 server 2」,沒有講訂閱哪個 channel、訊息格式、以及 Redis 掛了怎麼辦(第 7 段的 broker 單點)。實際動手接一次才知道 backplane 的邊界在哪。
YouTube 搜尋:Redis pub/sub WebSocket multiple servers socket.io redis adapter explained scaling websockets redis backplane
2. 往旁邊對照:Server-Sent Events 與 HTTP long polling 的擴展方式
第 2 段說 fallback transport 的擴展方式「跟 WebSocket 完全不同」,但沒有展開。對照 SSE / long polling 在 load balancer 與狀態上的差異,才能判斷自己的場景是否非用 WebSocket 不可。
YouTube 搜尋:WebSocket vs Server-Sent Events scaling HTTP long polling vs WebSocket SSE load balancer sticky sessions
3. 往上應用:load shedding、backoff 與連線恢復的系統設計
第 7 段點出 load shedding 與 thundering herd「很棘手」但沒說怎麼做。這正是把影片內容用在真實系統時最先撞到的問題:客戶端重連策略(exponential backoff、jitter)與伺服器端的過載保護要一起設計。
YouTube 搜尋:thundering herd problem websocket reconnect exponential backoff with jitter load shedding system design
- 接著看 ←15 Fullstack Concepts Every Frontend Should Know · WebSocket、round robin、單點故障在那站點名,這站把它們拉成一條擴展推理
- 接著看 ←How Frontend Engineers can master the Full-stack · 垂直/水平擴展與 WebSocket 在那站建立,這站專講兩者用在長連線的代價
- 接著看 →Message Queues in System Design Interviews w/ Meta Staff Engineer · backplane 用的 message broker 在這站只點到,MQ 站把 broker、replication 講完整
- 相關 —Real Frontend System Design (from a Senior Engineer) · 共用 load balancer、round robin、fault tolerance、horizontal scaling:一站專講 WebSocket,一站放進完整系統設計
- 相關 —Frontend System Design Explained w/ Senior Engineer (Microfrontends, Monorepo, MCP UI, Reactjs) · 共用 WebSocket、stateless、horizontal scaling:一站講後端怎麼擴,一站講前端架構怎麼接
- 接著看 ←Keep Those WebSocket Connections Alive! · heartbeat 解決單機的死連線,那站接著談多台 server 與 load balancer 怎麼擴
- 接著看 ←How Web Sockets work | Deep Dive · 這站講完一條連線怎麼建、資料怎麼走,那站講百萬條連線怎麼撐
- 接著看 ←WebSockets Aren’t as Reliable as You Think.. Here's Why · 這站停在兩三台 server 加 nginx 與 broker,那站放大到百萬連線