跳至主要內容
Skip to content

分散式集群方案:Redis Pub/Sub 與水平擴展

WebSocket 是「有狀態(Stateful)」的。這與 HTTP 的「無狀態(Stateless)」本質截然不同。

在 WebSocket 中,一旦連線建立在 Server A,該用戶所有的訊息就只能進到 Server A。這引發了一個問題:如果用戶分佈在不同伺服器,他們該如何交換訊息? 本章我們將揭開「分散式即時架構」的面紗。


一、 核心挑戰:狀態落在哪?

  1. 連線粘滯 (Sticky Sessions):負載平衡器必須確保同一個 Client 始終被導向同一台伺服器。
  2. 訊息同步 (Message Broadcasting):必須有一個「中央廣播系統」來串接所有伺服器節點。

二、 方案:Redis Pub/Sub 轉接層

Redis 內建的 發布/訂閱 (Publish/Subscribe) 機制是實現跨節點通訊的最佳拍檔。

實戰演練:原生 Redis 廣播器

我們直接用 ws 庫與 ioredis 來實作跨服廣播。

javascript
const WebSocket = require("ws");
const Redis = require("ioredis");

const pub = new Redis();
const sub = new Redis();
const wss = new WebSocket.Server({ port: 8080 });

sub.subscribe("CHAT_CHANNEL");

sub.on("message", (channel, message) => {
  const data = JSON.parse(message);
  wss.clients.forEach((client) => {
    if (client.readyState === WebSocket.OPEN) client.send(JSON.stringify(data));
  });
});

wss.on("connection", (socket) => {
  socket.on("message", (raw) => {
    const data = JSON.parse(raw);
    pub.publish(
      "CHAT_CHANNEL",
      JSON.stringify({
        user: data.user,
        content: data.content,
      })
    );
  });
});

三、 規模化的下一步:負載平衡

在生產環境中,你通常會讓 Nginx 或是 AWS ALB 站在最前面:

  • 開啟 Sticky Sessions:通常基於 Cookie。
  • 支援 Upgrade 協議:確保負載平衡器不會中斷握手請求。

總結

分散式架構將有狀態的連線轉化為全局可達的數據流,讓系統具備了無限水平擴展的可能性。

下一章,我們將探討壓力測試與觀察性:Artillery 與 Grafana 儀表板。


️ 進階挑戰

  1. 實作挑戰:試著啟動兩個不同 Port (如 8080 與 8081) 的上述伺服器實例,並開啟兩個 Chrome 視窗分別連線,觀察訊息是否能跨 Server 同步。
  2. 穩定性思考:如果 Redis 突然斷線了,你的 WebSocket 集群會發生什麼事?我們該如何設計降級方案?

延伸閱讀與資源