我正在尝试使用this example中的Node.js实现群集和粘性会话。
但是,每当客户端连接到localhost:3000
时,无论已连接多少客户端,它们都将连接到Worker 2。
这是我使用的代码。
var express = require('express'),
cluster = require('cluster'),
net = require('net'),
sio = require('socket.io'),
sio_redis = require('socket.io-redis');
var port = 3000,
num_processes = require('os').cpus().length;
if (cluster.isMaster) {
// This stores our workers. We need to keep them to be able to reference
// them based on source IP address. It's also useful for auto-restart,
// for example.
var workers = [];
// Helper function for spawning worker at index 'i'.
var spawn = function(i) {
workers[i] = cluster.fork();
// Optional: Restart worker on exit
workers[i].on('exit', function(worker, code, signal) {
console.log('respawning worker', i);
spawn(i);
});
};
// Spawn workers.
for (var i = 0; i < num_processes; i++) {
spawn(i);
}
// Helper function for getting a worker index based on IP address.
// This is a hot path so it should be really fast. The way it works
// is by converting the IP address to a number by removing the dots,
// then compressing it to the number of slots we have.
//
// Compared against "real" hashing (from the sticky-session code) and
// "real" IP number conversion, this function is on par in terms of
// worker index distribution only much faster.
var workerIndex = function (ip, len) {
var _ip = ip.split(/['.'|':']/),
arr = [];
for (el in _ip) {
if (_ip[el] == '') {
arr.push(0);
}
else {
arr.push(parseInt(_ip[el], 16));
}
}
return Number(arr.join('')) % len;
}
// Create the outside facing server listening on our port.
var server = net.createServer({ pauseOnConnect: true }, function(connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[workerIndex(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(port);
} else {
// Note we don't use a port here because the master listens on it for us.
var app = new express();
// Here you might use middleware, attach routes, etc.
// Don't expose our internal server to the outside.
app.use(express.static(__dirname +'/public'));//set the root directory for web visitors
var server = app.listen(0, 'localhost'),
io = sio(server);
// Tell Socket.IO to use the redis adapter. By default, the redis
// server is assumed to be on localhost:6379. You don't have to
// specify them explicitly unless you want to change them.
io.adapter(sio_redis({ host: 'localhost', port: 6379 }));
console.log('starting worker: '+ cluster.worker.id);
io.sockets.on('connection', function(socket) {
console.log('connected to worker: ' + cluster.worker.id);
});
// Here you might use Socket.IO middleware for authorization etc.
// Listen to messages sent from the master. Ignore everything else.
process.on('message', function(message, connection) {
if (message !== 'sticky-session:connection') {
return;
}
// Emulate a connection event on the server by emitting the
// event with the connection the master sent us.
server.emit('connection', connection);
connection.resume();
});
}
当我运行它时,我看到了
starting worker: 1
starting worker: 2
starting worker: 3
starting worker: 4
但每当我通过socket.io连接到localhost:3000
时,无论是从html页面还是使用其他服务器作为客户端
var io = require('socket.io-client');
var socket = io.connect('http://localhost:3000');
我所看到的只是
connected to worker: 2
即使我同时从多个窗口和不同的浏览器连接。
我在工作区中添加了一个间隔,以查看其他工作人员在创建后是否还活着。
setInterval(function() {
console.log("I, worker " + cluster.worker.id + ", am alive");
}, 1000);
所有人都在每一秒回答这个问题。所以我想也许CPU根本不需要将连接客户端发送给另一个工作人员。所以我通过浏览器(基本上是游戏)通过浏览器添加了一个实际的应用程序给每个工作者。我使用火炮向游戏发送越来越多的玩家,我可能会遇到滞后,在某些时候它不再平稳,终端显示所有连接的客户端只会转到Worker 2
。 (请注意,如果我手动将num_processes
设置为1,则连接将按原样连接到worker 1,但只要我将其设置为2或更多,所有连接总是转到worker 2)。
有什么我需要做的事情让所有其他工人平等使用吗?或者有什么我错了吗?
答案 0 :(得分:0)
我之前没有考虑过这个问题感觉不好,但答案比我想象的要简单:上面的算法使用连接IP地址来确定将其分配给哪个工作人员。使用不同的浏览器没有改变任何东西,因为我仍然从同一台计算机连接。
顺便说一下,我在主人
中谈论这个功能var workerIndex = function (ip, len) {
var _ip = ip.split(/['.'|':']/),
arr = [];
for (el in _ip) {
if (_ip[el] == '') {
arr.push(0);
}
else {
arr.push(parseInt(_ip[el], 16));
}
}
return Number(arr.join('')) % len;
}
由于master正在侦听端口上的连接,而不是worker,因此也可以决定哪个worker处理哪个连接:
var server = net.createServer({ pauseOnConnect: true }, function(connection) {
// We received a connection and need to pass it to the appropriate
// worker. Get the worker for this connection's source IP and pass
// it the connection.
var worker = workers[workerIndex(connection.remoteAddress, num_processes)];
worker.send('sticky-session:connection', connection);
}).listen(port);
当我将其联机并从不同设备连接时,它运行良好。 我唯一担心的是,如果在某些时候所有连接的IP地址在格式化之后归结为4(或其他任何)的倍数,则一个工作人员将具有更重的负载。但这可能是一个很难发生的边缘情况。
无论如何,我知道其他人发布了类似的问题但没有得到答复。我希望这能以正确的方式指导某人。
感谢一大堆jfriend00的帮助!