socket.io+redis+expressjs 集群 - 在 expressjs 请求中获取套接字对象

Posted

技术标签:

【中文标题】socket.io+redis+expressjs 集群 - 在 expressjs 请求中获取套接字对象【英文标题】:socket.io+redis+expressjs cluster - get socket object in expressjs request 【发布时间】:2016-05-20 16:43:51 【问题描述】:

基于此答案的问题: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://localhost,worker 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

【问题讨论】:

【参考方案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!');
);

【讨论】:

以上是关于socket.io+redis+expressjs 集群 - 在 expressjs 请求中获取套接字对象的主要内容,如果未能解决你的问题,请参考以下文章

expressjs 的 socket.io 设置

expressJS 和 socket.io 监听不同的端口和 socket.io 客户端连接

带有 socket.io 和 expressjs 的节点集群

如何在同一个端口上使用 ExpressJS 和 Socket.io?

我的 ExpressJS 网站和 socket.io 端口可以使用相同的端口吗?

在 ExpressJS 中将 socket.io 实现为子模块的最佳方法是啥?