使用集群将 Socket.IO 扩展到多个 Node.js 进程时的一些问题

Posted

技术标签:

【中文标题】使用集群将 Socket.IO 扩展到多个 Node.js 进程时的一些问题【英文标题】:Some problems when scaling Socket.IO to multiple Node.js processes using cluster 【发布时间】:2017-07-31 14:22:18 【问题描述】:

我的 node.js 服务器使用集群模块来处理多个进程。

如果服务器使用 Socket.IO 接收来自客户端的请求,它会将数据传送到另一个使用 redis 发布的服务器。它通过 redis subscribe 接收精炼的数据,然后将这些数据扔给客户端。

我使用一个节点进程通过redis sub接收数据,其他进程通过socket.io向客户端发送数据。

当页面加载时客户端连接socket.io。 在这里,这是我的问题。 连接事件反复发生,甚至没有加载页面。 当客户端连接时,我从该套接字获取 socket.id,然后当我想向该客户端套接字发送数据时使用它。但是这种连接反复发生,我认为客户端使用的套接字发生了变化。所以,我记得的第一个 socket.id 是没用的。我无法从那个 socket.id 发送数据。我在socket对象中存储了auth信息,所以改变的客户端socket是没有用的。

index.pug

    $(document).ready(function()
       var socket = io.connect();
       (...)

app.js

    var cluster = require('cluster');
    var socketio = require('socket.io');
    var NRP = require('node-redis-pubsub');
    var nrpForChat = new NRP(config.chatRedisConfig);
    var nrpForCluster = new NRP(config.clusterRedisConfig);



    var startExpressServer = function()
      var app = express();
      var server = require('http').createServer(app);
      var io = socketio.listen(server);
      var redis = require('socket.io-redis');
      io.adapter(redis( host: 'localhost', port: 6380 ));


      io.sockets.on('connection', function(socket)
        socketController.onConnect(io, socket, nrpForChat);
      );


      server.listen(config.port, function()
        console.log('Server app listening on port '+config.port);
      );


      nrpForCluster.on('to:others:proc', function(data)
        var socket = io.sockets.connected[data.target.sockid];
        if (socket) 
          if (data.event == '_net_auth') 
            if (data.data.res)
              socket.enterId = data.data.data.enterId;
              socket.memberKey = data.data.data.memberKey;
              socket.sid = data.data.data.sid;
              socket.emit(data.event, data.data);
            else
              console.log('auth failed.');
            
               
         else 
          socket.emit(data.event, data.data);
        
      );

      module.exports = app;
    


    var numCpus = require('os').cpus().length;

    if (cluster.isMaster) 
      for (var i = 0; i < numCpus; i++) 
        cluster.fork();
      
    
    else 
      if (cluster.worker.id == numCpus) 

        nrpForChat.on('chat:to:relay', function(data)
          nrpForCluster.emit('to:others:proc', data);
        );

        if (numCpus == 1) 
          startExpressServer();
        
      
      else 
        startExpressServer();
      
    

【问题讨论】:

看到这个link 【参考方案1】:

默认情况下,socket.io 会连接多个连续的 http 请求。它基本上以 HTTP 轮询模式开始,然后在一些初始数据交换之后,它切换到 webSocket 传输。

因此,没有任何类型的粘性负载平衡的集群将无法工作。每个初始连续的 http 请求都应该发送到同一个服务器进程,很可能会被发送到集群中的不同服务器进程,初始连接将无法正常工作。

我知道有两种解决方案:

    实现某种粘性负载平衡(在集群模块中),以便每个客户端重复地转到同一个服务器进程,因此在连接开始时所有连续的 http 请求都将转到同一个服务器进程.

    切换您的客户端配置以立即切换到 webSocket 传输,并且从不使用 HTTP 轮询。连接仍会以 http 请求开始(因为所有 webSocket 连接都是这样开始的),但完全相同的连接将升级为 webSocket,因此永远只有一个连接。

仅供参考,您还需要确保 socket.io 中的重新连接逻辑正确地重新连接到所连接的原始服务器进程。

socket.io 结合 redis 支持 node.js 集群。虽然 socket.io 文档站点已经关闭了好几天,但您可以找到一些信息 here 和 Scaling Socket.IO to multiple Node.js processes using cluster 以及这里的 previously cached version of the socket.io doc for clustering。

【讨论】:

真的很有帮助!谢谢!

以上是关于使用集群将 Socket.IO 扩展到多个 Node.js 进程时的一些问题的主要内容,如果未能解决你的问题,请参考以下文章

在 Heroku 上使用集群和 socket.io-redis 扩展 node.js socket.io@1.*.*

socket.io 的可扩展架构

在多核服务器中使用带有集群的 socket.io 的好方法?

带有Node.js的HTML5画布,集群和Socket.IO

使用Socket.io将消息发送到多个房间?

将 pm2 的集群模块与 socket.io 和 socket.io-redis 一起使用