创新方案
用 WebSocket/Socket.IO…
当需要双向实时消息、房间管理、在线状态追踪并计划横向扩展时,用 WebSocket/Socket.IO 实现服务端与客户端。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 WebSocket/Socket.I通用
解决思路
- 分析需求:确定连接规模、消息量和延迟要求。
- 设计架构:规划集群、pub/sub、状态管理和故障转移。
- 实现服务端:构建带认证、房间和事件的 WebSocket 服务器。
- 本地验证:扩展前用 `npx wscat -c ws://localhost:3000` 测试连接处理、认证和房间行为,确认缺失/无效 token 被拒绝、加入/离开房间事件正常、消息可送达。
- 扩展验证:启用 adapter 前验证 Redis 连接和 pub/sub 往返。
- 配置 sticky sessions:用测试连接跨多个实例确认 sticky sessions,再设置负载均衡。
- 监控:跟踪连接数、延迟、吞吐量和错误率,为连接数突增和错误率阈值添加告警。
- 协议参考:加载 `references/protocol.md`,覆盖 WebSocket 握手、帧、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`,覆盖 SSE、long polling、何时选择 WebSockets。
- 服务端初始化:创建 HTTP server 和 Socket.IO Server,配置 CORS origin/credentials、`pingTimeout: 20000`、`pingInterval: 25000`。
- 认证中间件:用 `io.use` 在连接建立前读取 `socket.handshake.auth.token`;无 token 返回 `Authentication required`;用 `jwt.verify(token, process.env.JWT_SECRET)` 校验,成功写入 `socket.data.user`,失败返回 `Invalid token`。
- 启用 Redis adapter:用 `REDIS_URL` 创建 `pubClient`,复制 `subClient`,`await Promise.all([pubClient.connect(), subClient.connect()])`,再 `io.adapter(createAdapter(pubClient, subClient))`。
- 连接处理:在 `connection` 事件中取 `userId` 并记录日志,用 `pubClient.hSet('presence', userId, socket.id)` 标记在线。
- 房间处理:实现 `join-room`,调用 `socket.join(roomId)` 并向房间内其他人广播 `user-joined`;消息事件用 `io.to(roomId).emit('message', { userId, text, ts: Date.now() })` 发送。
- 断开清理:在 `disconnect` 中调用 `pubClient.hDel('presence', userId)` 并记录日志。
- 客户端连接:用 `io('wss://api.example.com', { auth: { token: getAuthToken() }, reconnection: true, reconnectionAttempts: 10, reconnectionDelay: 1000, reconnectionDelayMax: 30000, randomizationFactor: 0.5 })` 建立带认证和指数退避重连的连接。
- 客户端消息队列:断开时把消息推入 `messageQueue`;在 `connect` 事件中逐条 `socket.emit` 并清空队列,避免静默丢消息。
- 客户端重连处理:在 `disconnect` 中记录原因;若原因是 `io server disconnect` 则手动 `socket.connect()`;在 `connect_error` 中记录错误。
- 发送消息逻辑:`sendMessage(roomId, text)` 中若 `socket.connected` 直接 emit,否则推入队列等待重连。
- 负载均衡:必须使用 sticky sessions,因为 WebSocket 连接有状态,请求必须路由到同一服务实例。
- 心跳:实现 heartbeat/ping-pong 检测死连接,不能只依赖 TCP keepalive。
- 消息范围:用 rooms/namespaces 做消息范围控制,而不是在应用逻辑里过滤。
- 断线窗口:在断开期间排队消息,避免静默数据丢失。
- 容量规划:横向扩展前规划每实例连接上限。
- 状态存储:不要把大量状态存在内存里而不做集群策略,应使用 Redis 或外部存储。
- 端口处理:不要把 WebSocket 和 HTTP 混在同一端口而不做显式 upgrade 处理。
- 连接清理:必须处理连接清理,包括 presence 记录、房间成员关系和进行中的定时器。
- 负载测试:生产前做负载测试,因为连接数突增的行为与 HTTP 流量突增不同。
- 输出内容:实现 WebSocket 功能时,提供服务器设置、事件处理器(connection/message/disconnect)、客户端库(连接/事件/重连)和简要扩展策略说明。
适用边界 · 注意事项
- 只需要单向服务器推送时,优先考虑 SSE,而不是 WebSocket。
- 消息频率很低或只是简单请求/响应时,优先用长轮询或 HTTP。
- 无法提供 sticky sessions 或共享状态存储时,不要直接做多实例水平扩展。