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断开事件错误地为其他用户触发,然后再次连接