WebSocket 在 JavaScript 中的应用:基于 Node.js 的高级实时应用

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

WebSocket 在 JavaScript 中的高级应用:从原型到生产环境

\n

WebSocket 是一种协议,通过一个持久的 TCP 连接在客户端和服务器之间提供全双工通信。在之前的文章中,我们讨论了基础知识:建立连接、发送消息和简单的聊天。然而,要构建真正可靠且可扩展的实时应用(在线游戏、交易平台、协作编辑系统),必须掌握高级技术。在本文中,我们将探讨 Node.js 上生产级 WebSocket 服务器的架构,包括扩展、状态管理、授权和错误处理。

\n\n

1. 高级服务器架构与房间管理

\n

最简单的 WebSocket 服务器(基于 ws 库)在一个全局对象中处理所有客户端。在实际项目中,需要逻辑隔离——房间和频道。

\n

考虑一个使用 RoomManager 类的架构示例:

\n
const WebSocket = require('ws');\n\nclass RoomManager {\n    constructor() {\n        this.rooms = new Map(); // roomName -> Set<WebSocket>\n    }\n\n    join(ws, roomName) {\n        if (!this.rooms.has(roomName)) {\n            this.rooms.set(roomName, new Set());\n        }\n        this.rooms.get(roomName).add(ws);\n        ws.room = roomName; // 在套接字上保存房间\n    }\n\n    leave(ws) {\n        if (ws.room && this.rooms.has(ws.room)) {\n            this.rooms.get(ws.room).delete(ws);\n            if (this.rooms.get(ws.room).size === 0) {\n                this.rooms.delete(ws.room);\n            }\n        }\n    }\n\n    broadcast(roomName, message, excludeWs = null) {\n        if (!this.rooms.has(roomName)) return;\n        this.rooms.get(roomName).forEach(client => {\n            if (client !== excludeWs && client.readyState === WebSocket.OPEN) {\n                client.send(JSON.stringify(message));\n            }\n        });\n    }\n}\n\n// 使用\nconst wss = new WebSocket.Server({ port: 8080 });\nconst roomManager = new RoomManager();\n\nwss.on('connection', (ws) => {\n    ws.on('message', (data) => {\n        const msg = JSON.parse(data);\n        switch (msg.type) {\n            case 'join':\n                roomManager.join(ws, msg.room);\n                break;\n            case 'message':\n                roomManager.broadcast(ws.room, { user: msg.user, text: msg.text }, ws);\n                break;\n        }\n    });\n\n    ws.on('close', () => roomManager.leave(ws));\n});
\n

这段代码允许创建隔离的聊天或游戏会话。请注意关闭连接的处理——这对于防止内存泄漏至关重要。

\n\n

2. 扩展:集群与 Redis Pub/Sub

\n

单个 Node.js 进程无法有效利用超过一个 CPU 核心。为了扩展,我们使用内置的 cluster 模块。然而,问题出现了:WebSocket 连接绑定到特定的工作进程。如何向连接到另一个工作进程的用户发送消息?

\n

解决方案是外部消息代理,例如 Redis Pub/Sub。

\n
const cluster = require('cluster');\nconst http = require('http');\nconst { Server } = require('ws');\nconst redis = require('redis');\n\nconst REDIS_CHANNEL = 'ws:messages';\n\nif (cluster.isMaster) {\n    // 根据核心数启动工作进程\n    const numCPUs = require('os').cpus().length;\n    for (let i = 0; i < numCPUs; i++) {\n        cluster.fork();\n    }\n\n    cluster.on('exit', (worker) => {\n        console.log(`Worker ${worker.process.pid} died`);\n        cluster.fork(); // 重启\n    });\n} else {\n    // 工作进程\n    const server = http.createServer();\n    const wss = new Server({ server });\n\n    // 连接到 Redis\n    const pub = redis.createClient();\n    const sub                

博客

书籍推荐