Socket.io 断开事件和相关闭包的垃圾收集

Posted

技术标签:

【中文标题】Socket.io 断开事件和相关闭包的垃圾收集【英文标题】:Socket.io disconnect events and garbage collection for related closure 【发布时间】:2015-10-19 18:20:08 【问题描述】:

我有一个使用 socket.io 的基本实时服务器。我的问题涉及闭包和垃圾收集,以及我是否应该费心将套接字连接存储在关联数组中,或者只是将其留给闭包来管理连接。

我遇到的一个问题是,如果套接字连接断开,如果在断开连接的套接字上调用 emit,同一个套接字连接是否会尝试发送消息?换句话说,当您在断开连接的套接字上调用 socket.emit() 时会发生什么?

这是一些代码,底部有问题:

 var socketio = require('socket.io);
 var io = socketio.listen(server);
 var EE = require('events').EventEmitter;
 var ee = new EE(); //this is actually initialized elsewhere but now you know what it is

 var connectedUsers = ; //associative array to store socket connections by socket.id

        io.on('connection', function (socket) 

           connectedUsers[socket.id] = socket;  //should I bother storing the socket connections in the associative array or just leave it to the closure to store them

            socket.on('disconnect', function () 
                connectedUsers[socket.id] = null; //should I bother removing
            );

            ee.on('update',function(data)

                socket.emit('update',JSON.stringify(data));

            );

            ee.on('insert',function(data)

                socket.emit('insert',JSON.stringify(data));

            );

            ee.on('delete',function(data)

                socket.emit('delete',JSON.stringify(data));

            );
        );
    

所以我的主要问题/担忧是,用于闭包的内存量将随着套接字连接的数量线性增长,并且永远不会停止或减少。 socket.io 断开连接事件是否通过垃圾回收释放内存?

看来我需要重新组织上面的代码来完成两件事:

    允许进行垃圾收集以便服务器内存 足迹不会无限增长

    避免发布所有数据 每一个socket连接,这就是它现在正在做的事情。

这准确吗?

【问题讨论】:

【参考方案1】:

我遇到的一个问题是,如果套接字连接断开,如果在断开连接的套接字上调用 emit,同一个套接字连接是否会尝试发送消息?换句话说,当您在断开连接的套接字上调用 socket.emit() 时会发生什么?

没有。在断开连接的套接字上对.emit() 的任何调用都将被忽略。 Here's the relevant source code snippet.

connectedUsers[socket.id] = socket;
//should I bother storing the socket connections in the
// associative array or just leave it to the closure to store them

您不应该存储它们。把它留给关闭。那么以后就不需要将它们设置为null了。

所以我的主要问题/担忧是,用于闭包的内存量将随着套接字连接的数量线性增长,并且永远不会停止或减少。 socket.io 断开连接事件是否通过垃圾回收释放内存?

是的,当套接字断开连接时,垃圾收集将被释放。 socket.io 将从其内部队列中删除它们,不再有任何引用它们,它们将有资格进行垃圾收集。但是,您上面的代码与您发送事件的方式不正确。详情见下文。

ee.on('update',function(data)
   socket.emit('update',JSON.stringify(data));
);

不要这样做。问题是您永远不会调用removeListener,因此在每个“更新”事件中,您将为自进程启动以来曾经连接过的每个套接字执行这些回调之一,甚至是断开连接的套接字。那是你的泄漏。相反,对于这种模式,只需在闭包之外设置一次广播:

ee.on('update', function (data) 
  io.sockets.emit('update', data);
);

(除此之外:JSON.stringify 几乎肯定是不必要的。只有当你真的知道你必须在另一边将它作为字符串获取时才这样做。只发出一个对象可能是正确的)

现在对于需要发送到特定套接字而不是所有连接的套接字的情况,您应该使用命名函数,以便您可以将每个 ee.on 与相应的 ee.removeListener 配对,以正确防止幻听侦听器。

io.on('connection', function(socket) 
  // every socket gets a closure
  // and a distinct onUpdate function object
  function onUpdate(data) 
    socket.emit('update', data);
  
  ee.on('update', onUpdate);

  socket.on('disconnect', function() 
    // removes the handler for this specific socket,
    // leaving the others intact
    ee.removeListener('update', onUpdate);
  );
);

【讨论】:

一旦我以某种方式确认信息,将选择作为答案:)

以上是关于Socket.io 断开事件和相关闭包的垃圾收集的主要内容,如果未能解决你的问题,请参考以下文章

如何在socket.io中的断开事件上获取断开连接的客户端的套接字ID

Socket.IO处理断开事件

垃圾收集导致连接的套接字延迟(NodeJS 服务器)

Socket.io断开事件错误地为其他用户触发,然后再次连接

使用 Angular 的单页应用程序引擎时依赖 socket.io/express.io 中的“断开连接”事件

socket io:如何传递参数来断开套接字事件?