websocket-engineer

websocket-engineer

热门

在构建基于WebSocket或Socket.IO的实时通信系统时使用。适用于双向消息传递、基于Redis的水平扩展、在线状态跟踪、房间管理。

1.1万Star
958Fork
更新于 2026/5/20
SKILL.md
readonly只读
name
websocket-engineer
description

在构建基于WebSocket或Socket.IO的实时通信系统时使用。适用于双向消息传递、基于Redis的水平扩展、在线状态跟踪、房间管理。

WebSocket 工程师

核心工作流程

  1. 分析需求 — 确定连接规模、消息量、延迟需求
  2. 设计架构 — 规划集群、发布/订阅、状态管理、故障转移
  3. 实现 — 构建带有身份验证、房间、事件的WebSocket服务器
  4. 本地验证 — 在扩展前测试连接处理、身份验证和房间行为(例如 npx wscat -c ws://localhost:3000);确认缺少或无效令牌时的身份验证拒绝、房间加入/离开事件以及消息投递
  5. 扩展 — 在启用适配器前验证Redis连接和发布/订阅往返;配置粘性会话并通过跨多个实例的测试连接确认;设置负载均衡
  6. 监控 — 跟踪连接数、延迟、吞吐量、错误率;为连接数峰值和错误率阈值添加告警

参考指南

根据上下文加载详细指导:

主题 参考 加载时机
协议 references/protocol.md WebSocket握手、帧、ping/pong、关闭码
扩展 references/scaling.md 水平扩展、Redis发布/订阅、粘性会话
模式 references/patterns.md 房间、命名空间、广播、确认
安全 references/security.md 身份验证、授权、速率限制、CORS
替代方案 references/alternatives.md SSE、长轮询、何时选择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})`);

  // 在线状态:标记用户在线
  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,       // 初始延迟(毫秒)
  reconnectionDelayMax: 30000,   // 上限30秒
  randomizationFactor: 0.5,      // 抖动以避免惊群效应
});

// 断开连接时排队消息
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); // 缓冲直到重连
  }
}

约束

必须做

  • 使用粘性会话进行负载均衡(WebSocket连接是有状态的——请求必须路由到同一服务器实例)
  • 实现心跳/ping-pong以检测死连接(仅TCP保活是不够的)
  • 使用房间/命名空间进行消息范围界定,而不是在应用逻辑中过滤
  • 在断开连接窗口期间对消息进行排队,以避免静默数据丢失
  • 在水平扩展之前规划每个实例的连接限制

禁止做

  • 在没有集群策略的情况下在内存中存储大量状态(使用Redis或外部存储)
  • 在没有显式升级处理的情况下在同一端口上混合WebSocket和HTTP
  • 忘记处理连接清理(在线状态记录、房间成员资格、进行中的定时器)
  • 在生产前跳过负载测试——连接数峰值的行为与HTTP流量峰值不同

输出模板

在实现WebSocket功能时,提供:

  1. 服务器设置(Socket.IO/ws配置)
  2. 事件处理器(连接、消息、断开)
  3. 客户端库(连接、事件、重连)
  4. 扩展策略的简要说明

知识参考

Socket.IO, ws, uWebSockets.js, Redis适配器, 粘性会话, nginx WebSocket代理, JWT over WebSocket, 房间/命名空间, 确认, 二进制数据, 压缩, 心跳, 背压, 水平Pod自动缩放

文档