websocket-engineer

websocket-engineer

熱門

建置基於 WebSocket 或 Socket.IO 的即時通訊系統時使用。適用於雙向訊息傳送、透過 Redis 進行水平擴充、在線狀態追蹤以及房間/聊天室管理等情境。

1.1萬星標
958分支
更新於 2026/5/20
SKILL.md
唯讀
名稱
websocket-engineer
描述

建置基於 WebSocket 或 Socket.IO 的即時通訊系統時使用。適用於雙向訊息傳送、透過 Redis 進行水平擴充、在線狀態追蹤以及房間/聊天室管理等情境。

WebSocket 工程師

核心工作流程

  1. 需求分析 — 評估連線規模、訊息流量與延遲需求
  2. 架構設計 — 規劃集群(clustering)、發布/訂閱(pub/sub)、狀態管理與故障移轉(failover)
  3. 實作開發 — 建置具備身份驗證、房間與事件處理機制的 WebSocket 伺服器
  4. 本地驗證 — 在進行擴充前,先測試連線處理、身份驗證及房間行為(例如:npx wscat -c ws://localhost:3000);確認缺少或無效 Token 時會正確拒絕連線、驗證加入/離開房間事件以及訊息傳遞
  5. 水平擴充 — 在啟用轉接器(adapter)前,先確認 Redis 連線與發布/訂閱往返測試(pub/sub round-trip);設定黏性工作階段(sticky sessions),並透過跨多個執行個體(instances)的測試連線進行驗證;建立負載平衡機制的設定
  6. 系統監控 — 追蹤連線數、延遲、吞吐量與錯誤率;針對連線數驟增及錯誤率超越門檻設定警報通知

參考指南

根據開發情境載入對應的詳細指引:

主題 參考文件 載入時機
協定細節 references/protocol.md WebSocket 握手(handshake)、數據框(frames)、ping/pong 機制、關閉代碼
擴充機制 references/scaling.md 水平擴充、Redis 發布/訂閱(pub/sub)、黏性工作階段(sticky sessions)
設計模式 references/patterns.md 房間(rooms)、命名空間(namespaces)、廣播(broadcasting)、送達確認(acknowledgments)
資安防護 references/security.md 身份驗證(Authentication)、授權(Authorization)、速率限制(rate limiting)、CORS
替代方案 references/alternatives.md Server-Sent Events (SSE)、長輪詢(long polling)、何時該選擇 WebSocket

程式碼範例

伺服器端設定(整合身份驗證與房間管理的 Socket.IO

import { createServer } from "http";
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";
import jwt from "jsonwebtoken";

const httpServer = createServer();
const io = new Server(httpServer, {
  cors: { origin: process.env.ALLOWED_ORIGIN, credentials: true },
  pingTimeout: 20000,
  pingInterval: 25000,
});

// 身份驗證中介軟體 — 在建立連線前執行
io.use((socket, next) => {
  const token = socket.handshake.auth.token;
  if (!token) return next(new Error("Authentication required"));
  try {
    socket.data.user = jwt.verify(token, process.env.JWT_SECRET);
    next();
  } catch {
    next(new Error("Invalid token"));
  }
});

// 用於水平擴充的 Redis 轉接器
const pubClient = createClient({ url: process.env.REDIS_URL });
const subClient = pubClient.duplicate();
await Promise.all([pubClient.connect(), subClient.connect()]);
io.adapter(createAdapter(pubClient, subClient));

io.on("connection", (socket) => {
  const { userId } = socket.data.user;
  console.log(`connected: ${userId} (${socket.id})`);

  // 線上狀態(Presence):將使用者標記為在線
  pubClient.hSet("presence", userId, socket.id);

  socket.on("join-room", (roomId) => {
    socket.join(roomId);
    socket.to(roomId).emit("user-joined", { userId });
  });

  socket.on("message", ({ roomId, text }) => {
    io.to(roomId).emit("message", { userId, text, ts: Date.now() });
  });

  socket.on("disconnect", () => {
    pubClient.hDel("presence", userId);
    console.log(`disconnected: ${userId}`);
  });
});

httpServer.listen(3000);

用戶端重新連線(搭配指數退避演算法)

import { io } from "socket.io-client";

const socket = io("wss://api.example.com", {
  auth: { token: getAuthToken() },
  reconnection: true,
  reconnectionAttempts: 10,
  reconnectionDelay: 1000,       // 初始延遲時間 (ms)
  reconnectionDelayMax: 30000,   // 上限設定為 30 秒
  randomizationFactor: 0.5,      // 加入隨機抖動因子,避免驚群效應 (thundering herd)
});

// 當處於斷線狀態時,將待傳送的訊息暫存至佇列
let messageQueue = [];

socket.on("connect", () => {
  console.log("connected:", socket.id);
  // 清空佇列並補發訊息
  messageQueue.forEach((msg) => socket.emit("message", msg));
  messageQueue = [];
});

socket.on("disconnect", (reason) => {
  console.warn("disconnected:", reason);
  if (reason === "io server disconnect") socket.connect(); // 手動觸發重新連線
});

socket.on("connect_error", (err) => {
  console.error("connection error:", err.message);
});

function sendMessage(roomId, text) {
  const msg = { roomId, text };
  if (socket.connected) {
    socket.emit("message", msg);
  } else {
    messageQueue.push(msg); // 暫存至緩衝佇列直到連線恢復
  }
}

開發規範與限制

務必執行 (MUST DO)

  • 在負載平衡中使用黏性工作階段(sticky sessions,因 WebSocket 連線屬於有狀態連線,請求必須精確路由至同一個伺服器執行個體)
  • 實作心跳機制/Ping-Pong 以即時偵測斷線狀態(僅靠 TCP keepalive 無法完整涵蓋所有異常)
  • 善用房間(rooms)與命名空間(namespaces)管理訊息作用域,避免在應用程式邏輯中進行無謂的過濾
  • 在斷線過渡期將訊息暫存至佇列,防止資料無聲遺失
  • 在進行水平擴充前,務必預先規劃單一執行個體的連線承載上限

嚴格禁止 (MUST NOT DO)

  • 在缺乏集群策略的情況下,直接將大量狀態常駐於記憶體中(應優先採用 Redis 或外部儲存)
  • 在未明確處理 Upgrade 協定轉換的情況下,讓 WebSocket 與 HTTP 共用同一 Port
  • 遺漏連線資源清理機制(如在線狀態紀錄、房間成員資格、未完成的計時器等)
  • 未經負載壓力測試即直接上線 — 連線數驟增的物理特性與傳統 HTTP 流量衝擊截然不同

輸出範本

實作 WebSocket 功能時,請提供:

  1. 伺服器端設定檔(Socket.IO / ws 相關配置)
  2. 事件處理常式(包含連線、訊息傳遞、斷線清理)
  3. 用戶端函式庫整合(包含連線建立、事件監聯與重連機制)
  4. 水平擴充策略簡要說明

知識庫參考

Socket.IO, ws, uWebSockets.js, Redis adapter, sticky sessions, nginx WebSocket proxy, JWT over WebSocket, rooms/namespaces, acknowledgments, binary data, compression, heartbeat, backpressure, horizontal pod autoscaling

Documentation