Node.js集群是否自带负载均衡?如何测试负载均衡有效性?
Node.js Cluster + Socket.io 负载均衡问题
问题描述
我有一台配备4核8逻辑处理器的CPU,使用以下Node.js集群代码创建了8个Worker进程和1个Master进程,但发现Socket连接都倾向于连接到最后一个Worker(对应CPU8)。想请教几个问题:
- 这种集群方式是否会自动实现负载均衡?
- 是否需要手动配置负载均衡?
- 有没有方法测试负载均衡是否正常工作?
我的代码
const os = require('os'), cluster = require('cluster'), cores = os.cpus(); var clusterCount = 0; if (cluster.isMaster) { console.log(`Master ${process.pid} is running`); // Fork workers for (let i = 0; i < cores.length; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`worker ${worker.process.pid} died`); }); } else { const http = require('http'), express = require('express'), socketio = require('socket.io'), process = require('process');; var cpu = cores[clusterCount]; var app = express(); var port = process.env.PORT || process.argv[2] || 8080; var server = app.listen(port); var io = socketio(server); io.adapter(socketioRedis({ host: config.redis_host, port: config.redis_port })); io.on('connection', (socket) => { console.log(`User ${socket.id} connected to worker ${process.pid}`); }); console.log(`Worker ${process.pid} started on port: ${port} | ${cpu.model}`); clusterCount++; }
解答
1. 这种集群方式是否会自动实现负载均衡?
Node.js的cluster模块默认提供两种负载均衡策略,但这里有几个关键细节需要注意:
- 系统支持
SO_REUSEPORT(Linux 3.9+、macOS):此时每个Worker会独立监听同一个端口,操作系统会用轮询策略把新连接分发给不同Worker,这是比较高效的方式。 - 系统不支持
SO_REUSEPORT:Master进程会负责监听端口,然后用轮询策略将连接转发给Worker。
但你的代码存在一个明显的bug:clusterCount是全局变量,但每个Worker进程是完全独立的,所以每个Worker启动时clusterCount初始值都是0,执行clusterCount++后也只会在自己的进程内生效,导致所有Worker的cpu变量都指向cores[0],这可能干扰你对Worker对应CPU的判断。
另外,Socket.io是长连接,一旦握手成功,连接会一直绑定到某个Worker,但新连接应该被均衡到不同Worker。你看到所有连接集中到最后一个Worker,大概率是默认负载均衡没生效——要么是代码问题,要么是Socket.io的握手会话关联导致的(不过你已经用了Redis适配器,理论上能解决会话共享问题)。
2. 是否需要手动配置负载均衡?
分两种情况:
- 如果你的系统支持
SO_REUSEPORT,且代码修正后,不需要手动配置,操作系统或Node.js的cluster模块会自动处理负载均衡。 - 如果系统不支持
SO_REUSEPORT,或者Socket.io连接仍出现集中问题,可以手动配置反向代理(比如Nginx),让每个Worker监听不同端口,Nginx用轮询策略将请求转发到各个Worker端口。
3. 如何测试负载均衡是否正常工作?
这里有几个实用的测试方法:
- 日志统计法:在每个Worker的
connection事件中,统计当前Worker的连接数(可以用一个本地计数器,或者把数据上报到Redis),定期打印每个Worker的连接数。如果连接数分布均匀,说明负载均衡有效。 - 模拟多连接:用
socket.io-client写一个测试脚本,循环创建100+个Socket连接,然后查看日志中各个Worker的PID出现次数,判断是否均衡。 - 进程监控:用
top或htop命令查看每个Worker进程的CPU使用率,如果负载均衡正常,各个Worker的CPU占用率应该相差不大。 - 修正代码验证:把
clusterCount替换为cluster.worker.id(Worker ID从1开始),修改Worker中CPU关联的代码为var cpu = cores[cluster.worker.id - 1];,然后启动服务,查看每个Worker对应的CPU是否正确,再观察连接分布。
你的代码需要修正的点
- 替换无效的
clusterCount:每个Worker的ID可以通过cluster.worker.id获取,修正CPU关联代码:
// 在else分支中 var cpu = cores[cluster.worker.id - 1]; // 因为cluster.worker.id从1开始
- 移除冗余的
process引入:process是Node.js全局变量,不需要手动require。 - 确认Redis适配器配置:确保
config.redis_host和config.redis_port是正确的,否则Worker之间无法共享会话,可能导致连接集中问题。
内容的提问来源于stack exchange,提问作者Vardan Betikyan
相关产品推荐
相关产品推荐

