Node.js Cluster模块可扩展WebSocket服务器,但必须在每个worker中显式监听upgrade事件、独立创建ws.Server实例,并借助Redis等外部存储实现跨进程状态共享与消息广播,否则将出现400错误、心跳中断或连接不均等问题。

直接上结论:Node.js cluster 模块能扩展 WebSocket 服务器,但必须绕过默认的连接分发陷阱——否则会出现连接被拒绝、升级失败、心跳中断等现象。关键不是“能不能用”,而是“怎么让每个 worker 正确处理 upgrade 事件”。
WebSocket 服务器在 cluster 下报 EADDRINUSE 或 400 错误
这是最常见症状:启动多个 worker 后,客户端发起 ws:// 连接时返回 400,或日志里反复出现 Error: listen EADDRINUSE: address already in use :::3000。根本原因不是端口冲突,而是 HTTP 服务器在收到 Upgrade 请求时,没有在所有 worker 中统一注册监听逻辑。
- HTTP 服务可以靠操作系统自动负载均衡,但 WebSocket 升级请求(
Connection: upgrade+Upgrade: websocket)必须由**同一个 worker 完成整个生命周期**:从接收upgrade、握手、到后续帧收发 - 如果主进程把
upgrade请求随机分给某个 worker,而该 worker 没有绑定server.on('upgrade', ...),就会直接返回 400 -
http.createServer()默认不处理upgrade,必须显式监听;且这个监听必须在每个 worker 内部完成,不能只在 master 做
worker 中必须手动监听 upgrade 事件并透传给 ws.Server
使用 ws 库(如 ws 或 uWebSockets.js)时,不能只靠 wsServer = new WebSocketServer({ server }) 就完事。因为 server 是共享的,但 upgrade 事件不会自动广播到所有 worker —— 它只会触发在实际接收到该请求的那个 worker 上。
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 每个 worker 都要独立创建
ws.Server实例,并在自己的http.Server上监听upgrade - 正确写法是:在
else分支(worker 进程)中,先调用server.on('upgrade', (req, socket, head) => { ... }),再把socket和req手动传给wsServer.handleUpgrade() - 示例片段(基于官方
ws):
const WebSocket = require('ws');
const http = require('http');
if (cluster.isMaster) {
// fork logic...
} else {
const server = http.createServer();
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', (req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit('connection', ws, req);
});
});
server.listen(3000);
}
stateless 设计 + 外部 session 共享是 cluster 下 WebSocket 的硬性前提
每个 worker 有独立内存,无法靠 Map 或全局变量存 client 列表。一旦你尝试在某个 worker 里 broadcast(),其他 worker 的连接就收不到消息 —— 这不是 bug,是 cluster 的设计事实。
- 所有会话状态(用户 ID、房间归属、心跳时间戳)必须落地到外部存储:Redis 是最常用选择,用
redis-pubsub或redis-streams实现跨 worker 广播 - 避免在 worker 内部做“全量 client 遍历”,改用 Redis 的
PUBLISH/PSUBSCRIBE机制中转消息 - 连接数统计、踢人、房间列表等操作,必须通过原子命令(如
INCR,SADD,SMEMBERS)协调,不能依赖本地缓存 - 如果用了
ws库的wss.clients,它只返回当前 worker 的连接,对集群无效
Windows 下 round-robin 负载不生效,需强制设置 schedulingPolicy
Linux/macOS 默认用操作系统调度(cluster.SCHED_NONE),连接基本均匀;但 Windows 默认是 cluster.SCHED_RR 却不生效,导致大部分连接落到第一个 worker,其余空闲 —— 表现为 CPU 使用率严重不均、部分 worker OOM。
- 必须在
cluster.isMaster分支开头显式设置:cluster.schedulingPolicy = cluster.SCHED_RR; - 或者更稳妥地统一设为:
cluster.setupPrimary({ schedulingPolicy: cluster.SCHED_RR }); - 不设的话,Windows 下可能只有 1–2 个 worker 接收连接,其余长期 idle,完全浪费多核
真正难的不是启动多个进程,而是让每个 worker 独立扛住 WebSocket 生命周期、不共享内存却保持状态一致、在不同系统上都稳定分发连接。这些点漏掉任何一个,cluster 就只是看着热闹,实际性能反而不如单进程。


















