【问题标题】:socket.io+redis+expressjs cluster - get socket object in expressjs requestsocket.io+redis+expressjs 集群 - 在 expressjs 请求中获取套接字对象
【发布时间】:2025-12-03 07:10:01
【问题描述】:

基于此答案的问题:https://*.com/a/18650183/4478897

我试图找到这个解决方案,但似乎没有什么能按我需要的方式工作。

集群expressjssocket.io我们可以使用redis共享会话并在io世界(io.sockets.on('connection',...)中发送io消息。问题是我们是否想在expressjs 世界(route.get/post)中发送消息(或使用简单的socket.join/leave)。

如果我们不使用集群,我们可以将客户端 socket 对象附加到 express request 对象(或简单地将 export io 对象),然后在任何 GET/POST 路由上随时使用它.

另一方面,如果我们进行聚类并使用上述方法获取expressjs 世界中的socket 对象,则有时socket 对象未定义,因为此客户端的socket 对象已初始化在其他worker

一些示例流程:

  • 客户端连接到http://localhostworker 1 处理此请求。
  • 页面加载后,客户端连接到socket.ioWorker 2 处理此连接。
  • 客户端执行 POST 并再次 worker 1worker X 处理此请求。

在这种情况下,当客户端执行 POST 时,只有 worker 2 知道此客户端的 socket 对象。所以这将得到一个未定义的socket 对象。

那么问题来了:

我们如何从任何worker 获取客户端socket 对象以在expressjs request 对象上重用它。

也许我的代码是错误的,但几乎就像上面提到的答案的链接。


注意事项

  • 不想使用某种代理。
  • 不想迁移到其他库(expressio、sockjs...)
  • 对不起我的英语:)

使用最后的nodejs、socket.io、expressjs、socket.io-redis、redis...版本

请不要犹豫!


更新 1

可能的解决方案,但仍需要测试。不知道这是否真的很好:解决方案。

  • 更新 3:我自己回答的工作代码

更新 2

与更新 1 类似,但使用 https://nodejs.org/dist/latest-v5.x/docs/api/cluster.html#cluster_event_message

【问题讨论】:

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


    【解决方案1】:

    remoteJoinremoteLeave 方法被添加到 socket.io-redis 3.0.0:

    io.adapter.remoteJoin('<my-id>', 'room1', function (err) {
      if (err) { /* unknown id */ }
      // success
    });
    
    io.adapter.remoteLeave('<my-id>', 'room1', function (err) {
      if (err) { /* unknown id */ }
      // success
    });
    

    注意:实现看起来很像答案above

    【讨论】:

    • 我看到了合并。还是谢谢你:D
    【解决方案2】:

    好吧,终于尝试了代码并且它可以工作(有一些拼写错误的修改和其他东西),但我确信在某个地方需要一个更好的代码。所以我愿意接受更多答案!

    当授权客户端套接字和其他一些东西时,此代码是我的 socket.io 模块的一部分......

      var redis = require("redis");
      var redisPub = redis.createClient();
      var redisSub = redis.createClient();
      var PubSubChannel = "clusterChannel";
    
      // Function that checks if this worker knows the socket object of this socketId.
      // If not, publish the message to all the other sockets (workers)
      io.socketDo = function (type, socketId, roomName) {
        if (typeof io.sockets.connected[socketId] != "undefined") {
          if (type === "join") {
            return io.sockets.connected[socketId].join(roomName);
          }
          if (type === "leave") {
            return io.sockets.connected[socketId].leave(roomName);
          }
        } else {
          redisPub.publish(
            PubSubChannel,
            JSON.stringify({
              type: type,
              socketId: '' + socketId,
              roomName: roomName
            })
          );
        }
      };
    
      // Subscribe to some channel
      redisSub.subscribe(PubSubChannel);
    
      // When this worker receive a message from channel "PubSubChannel" checks
      // if it have the socket object for this socketId and do the operation
      redisSub.on("message", function (channel, data) {
        data = JSON.parse(data);
        var type = data.type;
        var socketId = data.socketId;
        var roomName = data.roomName;
        if ((type === "join" || type === "leave") && channel == PubSubChannel){
          if (typeof io.sockets.connected[socketId] != "undefined") {
            if (type === "join") {
              return io.sockets.connected[socketId].join(roomName);
            }
            if (type === "leave") {
              return io.sockets.connected[socketId].leave(roomName);
            }
          }
        }
      });
    

    然后只需简单地导出模块并将其附加到您的 expressjs request =&gt; req.io = io

    // req.session.socketId value is fetched on "io.sockets.on('connection', function(socket) {" 
    // by express to socket.io using redis shared sessions
    app.get('/', function (req, res) {
        req.io.socketDo('join', req.session.socketId, 'someRoomToJoin');
    
        // IT WORKS!
        req.io.sockets.in('someRoomToJoin').emit('text'); 
    
        req.io.socketDo('leave', req.session.socketId, 'someRoomToLeave');
        res.send('Hello World!');
    });
    

    【讨论】: