我正在尝试用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但是,每当我通过localhost:3000连接到socket.io时,无论是从html页面还是使用另一台服务器作为客户端。
var io = require('socket.io-client');
var socket = io.connect('http://localhost:3000');我只看到
connected to worker: 2即使我同时从多个窗口和不同的浏览器连接。
我在worker块中放置了一个间隔,以查看其他工作人员在创建后是否还活着。
setInterval(function() {
console.log("I, worker " + cluster.worker.id + ", am alive");
}, 1000);他们每一秒都在回答这个问题。所以我想,也许CPU根本不需要将连接客户端发送到另一个工作人员。所以我给每个工作人员添加了一个实际的应用程序,通过浏览器提供视觉反馈(基本上是一个游戏)。我用大炮把越来越多的玩家派到游戏中去,我可能会经历一些滞后,在某一时刻,它将不再是一帆风顺的,不过,终端显示所有连接的客户只会去Worker 2。(请注意,如果手动将num_processes设置为1,则连接将按应有的方式转到worker 1,但一旦使其变为2或更多,所有连接都会转到worker 2)。
有什么我需要做的,使所有其他工人被平等地使用吗?还是我出了什么问题?
发布于 2017-10-08 03:20:56
以前没有想到这一点我感到很难过,但答案比我想象的要简单:上面的算法使用连接的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;
}由于主程序正在侦听端口上的连接,而不是侦听工作人员,所以在这里决定哪个工作人员处理哪个连接:
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的帮助!
https://stackoverflow.com/questions/46617561
复制相似问题