Socket.io和Heroku Node.js应用程序上的多个Dyno。在建立连接之前关闭WebSocket

时间:2014-10-06 13:30:09

标签: node.js heroku websocket redis socket.io

我正在构建一个使用Websockets部署到Heroku的应用程序。

当我只使用1个dyno时,websockets连接正常工作,但当我缩放到> 1时,我得到以下错误

  

POST   HTTP://****.herokuapp.com/socket.io/ EIO = 2及运输=轮询& T公司= 1412600135378-1&安培; SID = zQzJJ8oPo5p3yiwIAAAC   400(错误请求)socket.io-1.0.4.js:2

     

WebSocket连接到   'WS://****.herokuapp.com/socket.io/ EIO = 2及运输=网页套接字&安培; SID = zQzJJ8oPo5p3yiwIAAAC?   失败:在建立连接之前关闭WebSocket。   socket.io-1.0.4.js:2

我正在使用Redis适配器启用多个Web进程

var io = socket.listen(server);
var redisAdapter = require('socket.io-redis');
var redis = require('redis');

var pub = redis.createClient(18049, '[URI]', {auth_pass:"[PASS]"});
var sub = redis.createClient(18049, '[URI]', {detect_buffers: true, auth_pass:"[PASS]"} );

io.adapter( redisAdapter({pubClient: pub, subClient: sub}) );

这正在使用localhost(我正在使用foreman运行,正如Heroku所做的那样,我正在启动2个Web进程,与Heroku相同)。

在我实现redis适配器之前,我遇到了Web套接字握手错误,因此适配器产生了一些影响。它现在偶尔也在工作,我假设当套接字匹配相同的web dyno时。

我也试图启用粘性会话,但它永远不会有效。

var sticky = require('sticky-session');
sticky(1, server).listen(port, function (err) {
  if (err) {
    console.error(err);
    return process.exit(1);
  }
  console.log('Worker listening on %s', port);
});

5 个答案:

答案 0 :(得分:19)

我是Heroku的Node.js平台所有者。

WebSockets在Heroku上可以在多个动态游戏中开箱即用; socket.io(和其他实时库)使用回退到无状态进程,如xhr轮询,没有会话亲和力。

要扩展socket.io应用程序,请先按照socket.io中的所有说明进行操作:

然后,在您的应用上启用会话亲和力(这是一项免费功能):

答案 1 :(得分:4)

我花了一些时间尝试使socket.io在多服务器架构中工作,首先在Heroku上工作,然后在许多人建议的Openshift上工作。

使其适用于两种PAAS的唯一方法是禁用xhr-polling并在客户端和服务器上设置transports: ['websocket']

在Openshift上,你必须使用* .rhcloud.com服务器显式地将服务器的端口设置为8000(对于ws - 8443,对于socket.io客户端初始化的wss,如本文所述:{{3} }。

轮询策略在Heroku上不起作用,因为它不支持粘性会话(http://tamas.io/deploying-a-node-jssocket-io-app-to-openshift/),而在Openshift上它因为这个问题而失败:https://github.com/Automattic/engine.io/issues/261,希望很快得到解决。< / p>

因此,到目前为止我找到的唯一解决方案是禁用轮询并仅使用websocket传输。

为了做到这一点,请使用socket.io&gt; 1.0 服务器端:

var app = express();
var server = require('http').createServer(app);

var socketio = require('socket.io')(server, {
  path: '/socket.io-client'
});
socketio.set('transports', ['websocket']);

客户端:

var ioSocket = io('<your-openshift-app>.rhcloud.com:8000' || '<your-heroku-app>.herokuapp.com', {
    path: '/socket.io-client'
    transports: ['websocket']
})

希望这会有所帮助。

答案 2 :(得分:1)

您可能需要运行RedisStore:

var session = require('express-session');
var RedisStore = require('connect-redis')(session);

app.use(session({
    store: new RedisStore(options),
    secret: 'keyboard cat'
}));

每个早期的q:Multiple dynos on Heroku + socket.io broadcasts

答案 3 :(得分:1)

我知道这不是一个正常的答案,但我已经尝试让WebSockets在Heroku上工作超过一周。经过与客户支持的长时间对话后,我终于尝试了OpenShift。 Heroku WebSockets处于测试阶段,但OpenShift WebSockets稳定。我在一小时内完成了OpenShift的代码。

http://www.openshift.com

我不以任何方式与OpenShift有任何关系。我只是一个满意的(非付费)客户。

答案 4 :(得分:0)

我遇到了很多问题。有许多问题未能同时成为一场巨大的噩梦。请确保执行以下操作以在heroku上扩展socket.io:

  1. 如果您正在使用群集,请确保实施socketio-sticky-session或类似的内容
  2. 客户端的连接网址不应该是https://example.com/socket.io/?EIO=3&transport=polling,而是https://example.com/,尤其是我使用https,因为heroku支持它

  3. enable cors in socket.io

  4. specify only websocket connections
  5. 对于你和其他人来说,它可以是其中任何一个。

    如果您在设置粘性会话群集时遇到问题,请参阅我的工作代码

    var http = require('http');
    var cluster = require('cluster');
    var numCPUs = require('os').cpus().length;
    var sticky = require('socketio-sticky-session');
    var redis = require('socket.io-redis');
    var io;
    
    if(cluster.isMaster){
      console.log('Inside Master');
      // create the worker processes
      for (var i = 0; i < numCPUs ; i++){
        cluster.fork();
      }
    }
    else {
      // The worker code to be run is written inside
      // the sticky().
    }
    
    sticky(function(){
      // This code runs inside the workers.
      // The sticky-session balances connection between workers based on their ip.
      // So all the requests from the same client would go to the same worker.
      // If multiple browser windows are opened in the same client, all would be
      // redirected to the same worker.
      io = require('socket.io')({transports:'websocket', 'origins' : '*:*'});
      var server = http.createServer(function(req,res){
        res.end('socket.io');
      })
    
    
      io.listen(server);
      // The Redis server can also be used to store the socket state
      //io.adapter(redis({host:'localhost', port:6379}));
    
      console.log('Worker: '+cluster.worker.id);
        // when multiple workers are spawned, the client
        // cannot connect to the cloudlet.
    
        StartConnect(); //this function connects my mongodb, then calls a function with io.on('connection', ..... socket.on('message'...... in relation to the io variable above
    
        return server;
    }).listen(process.env.PORT || 4567, function(){
      console.log('Socket.io server is up ');
    });
    

    更多信息: 个人而言,它可以在不使用websockets的会话中完美地工作(我使用socket.io进行统一游戏。它只能从编辑器中完美运行!)。当通过浏览器连接chrome或firefox时,它会显示这些握手错误,以及错误503和400.