Nodejs Clustering Socket.io - 当有活跃的worker时,使用Worker 2

时间:2017-10-07 06:56:12

标签: node.js multithreading socket.io

我正在尝试使用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)。

有什么我需要做的事情让所有其他工人平等使用吗?或者有什么我错了吗?

1 个答案:

答案 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的帮助!