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

时间:2016-02-10 11:01:46

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

基于这个答案的问题:https://stackoverflow.com/a/18650183/4478897

我试图找到这个解决方案,但似乎没有任何方法可以按照我需要的方式工作。

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

如果我们不使用群集,我们可以将客户端socket对象提升到快速request对象(或简称export io对象),然后在任何GET / POST路线上的任何时间。

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

一些示例流程:

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

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

所以,问题是:

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

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

注释

  • 不想使用某种代理。
  • 不想迁移到其他图书馆(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

2 个答案:

答案 0 :(得分:1)

在socket.io-redis remoteJoin中添加了

remoteLeave3.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

答案 1 :(得分:-1)

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

当授权客户端套接字和其他一些东西时,此代码是我的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);
        }
      }
    }
  });

然后只需导出模块并将其附加到您的快递request => 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!');
});