Socket.io,集群,表达和同步事件

时间:2014-05-27 09:41:36

标签: node.js express socket.io cluster-computing

我有一个大问题的一个星期。我尝试转换我的node.JS项目实际上在单核上运行到多核与集群。

使用websockets,此时,我对事件没有任何问题,但是,对于xhr-polling或jsonp-polling,我在集群模式下遇到socket.io的大问题。

这是我的服务器配置:

00-generic.js

'use strict';

var http            = require('http'),
    os              = require('os'),
    cluster         = require('cluster');

module.exports = function(done) {
    var app = this.express,
        port = process.env.PORT || 3000,
        address = '0.0.0.0';

    if(this.env == 'test'){
        port = 3030;
    }

    var self = this;
    var size = os.cpus().length;

    if (cluster.isMaster) {
        console.info('Creating HTTP server cluster with %d workers', size);

        for (var i = 0; i < size; ++i) {
            console.log('spawning worker process %d', (i + 1));
            cluster.fork();
        }

        cluster.on('fork', function(worker) {
            console.log('worker %s spawned', worker.id);
        });
        cluster.on('online', function(worker) {
            console.log('worker %s online', worker.id);
        });
        cluster.on('listening', function(worker, addr) {
            console.log('worker %s listening on %s:%d', worker.id, addr.address, addr.port);
        });
        cluster.on('disconnect', function(worker) {
            console.log('worker %s disconnected', worker.id);
        });
        cluster.on('exit', function(worker, code, signal) {
            console.log('worker %s died (%s)', worker.id, signal || code);
            if (!worker.suicide) {
                console.log('restarting worker');
                cluster.fork();
            }
        });
    } else {
        http.createServer(app).listen(port, address, function() {
            var addr = this.address();
            console.log('listening on %s:%d', addr.address, addr.port);
            self.server = this;
            done();
        });
    }
};

03-socket.io.js

"use strict";
var _               = require('underscore'),
    socketio        = require('socket.io'),
    locomotive      = require('locomotive'),
    RedisStore      = require("socket.io/lib/stores/redis"),
    redis           = require("socket.io/node_modules/redis"),
    v1              = require(__dirname + '/../app/socket.io/v1'),
    sockets         = require(__dirname + '/../../app/socket/socket'),
    config          = require(__dirname + '/../app/global'),
    cluster         = require('cluster');

module.exports = function () {
    if (!cluster.isMaster) {
        this.io = socketio.listen(this.server);

        var pub             = redis.createClient(),
            sub             = redis.createClient(),
            client          = redis.createClient();

        this.io.enable('browser client minification');  // send minified client
        this.io.enable('browser client etag');          // apply etag caching logic based on version number
        this.io.enable('browser client gzip');          // gzip the file

        this.io.set("store", new RedisStore({
            redisPub        : pub,
            redisSub        : sub,
            redisClient     : client
        }));
        this.io.set('log level', 2);
        this.io.set('transports', [
            'websocket',
            'jsonp-polling'
        ]);
        this.io.set('close timeout', 24*60*60);
        this.io.set('heartbeat timeout', 24*60*60);

        this.io.sockets.on('connection', function (socket) {
            console.log('connected with ' + this.io.transports[socket.id].name);

            // partie v1 @deprecated
            v1.events(socket);

            // partie v1.1 refaite
            _.each(sockets['1.1'], function(Mod) {
                var mod = new Mod();
                mod.launch({
                    socket  : socket,
                    io      : this.io
                });
            }, this);

        }.bind(this));
    }
};

通过轮询,客户端会不时地在与启动的侦听器不同的进程上进行连接。同样,通信服务器向客户端发射。

通过一点点搜索,我发现有必要通过store.io的商店来共享数据连接。所以我构建了RedisStore socket.io,如文档中所示,但即便如此,我发现自己的事件没有安全到达,我仍然收到此错误消息:

warn: client not handshaken client should reconnect

修改

现在,没有调用警告错误。我将redisStore更改为socket.io-clusterhub但是现在,并不总是调用事件。有时,好像轮询请求被另一个工作者捕获而不是启动了听众,因此没有任何反应。这是新配置:

'use strict';

var http            = require('http'),
    locomotive      = require('locomotive'),
    os              = require('os'),
    cluster         = require('cluster'),
    config          = require(__dirname + '/../app/global'),
    _               = require('underscore'),
    socketio        = require('socket.io'),
    v1              = require(__dirname + '/../app/socket.io/v1'),
    sockets         = require(__dirname + '/../../app/socket/socket');

module.exports = function(done) {
    var app = this.express,
        port = process.env.PORT || 3000,
        address = '0.0.0.0';

    if(this.env == 'test'){
        port = 3030;
    }

    var self = this;
    var size = os.cpus().length;

    this.clusterStore = new (require('socket.io-clusterhub'));

    if (cluster.isMaster) {
        for (var i = 0; i < size; ++i) {
            console.log('spawning worker process %d', (i + 1));
            cluster.fork();
        }

        cluster.on('fork', function(worker) {
            console.log('worker %s spawned', worker.id);
        });
        cluster.on('online', function(worker) {
            console.log('worker %s online', worker.id);
        });
        cluster.on('listening', function(worker, addr) {
            console.log('worker %s listening on %s:%d', worker.id, addr.address, addr.port);
        });
        cluster.on('disconnect', function(worker) {
            console.log('worker %s disconnected', worker.id);
        });
        cluster.on('exit', function(worker, code, signal) {
            console.log('worker %s died (%s)', worker.id, signal || code);
            if (!worker.suicide) {
                console.log('restarting worker');
                cluster.fork();
            }
        });
    } else {
        var server = http.createServer(app);

        this.io = socketio.listen(server);

        this.io.configure(function() {
            this.io.enable('browser client minification');  // send minified client
            this.io.enable('browser client etag');          // apply etag caching logic based on version number
            this.io.enable('browser client gzip');          // gzip the file

            this.io.set('store', this.clusterStore);
            this.io.set('log level', 2);
            this.io.set('transports', [
                'websocket',
                'jsonp-polling'
            ]);
            //this.io.set('close timeout', 24*60*60);
            //this.io.set('heartbeat timeout', 24*60*60);
        }.bind(this));

        this.io.sockets.on('connection', function (socket) {
            console.log('connected with ' + this.io.transports[socket.id].name);
            console.log('connected to worker: ' + cluster.worker.id);

            // partie v1 @deprecated
            v1.events(socket);

            // partie v1.1 refaite
            _.each(sockets['1.1'], function(Mod) {
                var mod = new Mod();
                mod.launch({
                    socket  : socket,
                    io      : this.io
                });
            }, this);

        }.bind(this));

        server.listen(port, address, function() {
            var addr = this.address();
            console.log('listening on %s:%d', addr.address, addr.port);
            self.server = this;
            done();
        });
    }
};

3 个答案:

答案 0 :(得分:2)

从该来源:http://socket.io/docs/using-multiple-nodes/

  

如果您计划分配不同的连接负载   进程或机器,您必须确保请求关联   具有特定会话ID连接到发起的进程   它们。

     

这是由于某些传输,如XHR轮询或JSONP轮询   依赖于在生命周期内发出几个请求   “插座”。

每次都要将连接路由到同一个工作人员:

<强>粘性会话

在socket.io文档中,这是每次都将请求路由到同一个worker的推荐方法。

https://github.com/indutny/sticky-session

  

将socket.io与群集一起使用的简单高效方法。

     

Socket.io正在执行多个请求来执行握手和   与客户建立联系。对于群集,这些请求可能会   到达不同的工作人员,这将打破握手协议。

var sticky = require('sticky-sesion');

sticky(function() {
  // This code will be executed only in slave workers

  var http = require('http'),
      io = require('socket.io');

  var server = http.createServer(function(req, res) {
    // ....
  });
  io.listen(server);

  return server;
}).listen(3000, function() {
  console.log('server started on 3000 port');
});

在节点之间传递消息:

<强> socket.io-redis的

在socket.io文档中,这是在工作者之间共享消息的推荐方法。

https://github.com/automattic/socket.io-redis

  

通过使用socket.io-redis适配器运行socket.io,您可以运行   不同进程或服务器中的多个socket.io实例   所有人都可以相互广播和发送事件。

socket.io-redis以这种方式使用:

var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

同时

我认为你没有使用socket.io v1.0.0。您可能希望更新您的版本以获得更高的稳定性。

您可以在http://socket.io/docs/migrating-from-0-9/

查看他们的迁移指南

答案 1 :(得分:2)

使用

时,socket.io文档中缺少一个步骤
var io = require('socket.io')(3000);
var redis = require('socket.io-redis');
io.adapter(redis({ host: 'localhost', port: 6379 }));

您需要告诉客户您要使用&#39; websockets&#39;作为唯一的传输形式或它不起作用...所以对于客户端的构造函数使用

io.connect(yourURL , { transports : ['websocket']});

在这里看到我对类似问题的回答(我的回答可能更适合这个帖子): https://stackoverflow.com/a/30791006/4127352

答案 2 :(得分:1)

以下代码对我来说,这是socket.io谁创建了集群,我在config.clusterSticky上设置true以激活兼容性集群和socket.io

'use strict';

/*
 var cl = console.log;
 console.log = function(){
 console.trace();
 cl.apply(console,arguments);
 };
 */

var cluster = require('cluster'),
    config = require('./config/all'),
    deferred = require('q').defer(),
    express = require('express'),
    app = express(),
    http = require('http'),
    sticky = require('socketio-sticky-session'),
    io = require('socket.io');

// Code to run if we're in the master process or if we are not in debug mode/ running tests

if ((cluster.isMaster) &&
    (process.execArgv.indexOf('--debug') < 0) &&
    (process.env.NODE_ENV !== 'test') && (process.env.NODE_ENV !== 'development') &&
    (process.execArgv.indexOf('--singleProcess') < 0) &&
    (!config.clusterSticky)) {

    console.log('for real!');
    // Count the machine's CPUs
    var cpuCount = process.env.CPU_COUNT || require('os').cpus().length;

    // Create a worker for each CPU
    for (var i = 0; i < cpuCount; i += 1) {
        console.log('forking ', i);
        cluster.fork();
    }

    // Listen for dying workers
    cluster.on('exit', function (worker) {
        // Replace the dead worker, we're not sentimental
        console.log('Worker ' + worker.id + ' died :(');
        cluster.fork();
    });

// Code to run if we're in a worker process
} else {
    var port = config.http.port;
    var workerId = 0;
    if (!cluster.isMaster) {
        workerId = cluster.worker.id;
    }

    var server = http.createServer(app);
    io.listen(server);

    //TODO routes etc (core)

    server.on('listening', function () {
        console.log('Slave app started on port ' + port + ' (' + process.env.NODE_ENV + ') cluster.worker.id:', workerId);
    });

    if(config.clusterSticky && (process.env.NODE_ENV !== 'test') && (process.env.NODE_ENV !== 'development')) {
        sticky(server).listen(port);
    } else {
        server.listen(port);
    }

    deferred.resolve(server);
}

module.exports = deferred.promise;