关于 `socket.setTimeout` 在高压请求下,Nodejs会出现 `EventEmitter memory leak detected` 的问题讨论
关于 socket.setTimeout
在高压请求下,Nodejs会出现 EventEmitter memory leak detected
的问题讨论
最近又遇到 EventEmitter memory leak detected
问题了。
原因是超时使用了 socket.setTimeout()
,在压测的时候出现 #issues3 EventEmitter内存可能泄漏的异常。
于是我又想起了之前分享过的关于超时设置的问题。
我使用 setTimeout()
替换 socket.setTimeout()
后,此问题就不再出现了。
看了一下 net.js
的源代码,感觉是不会出问题的,但是实际输出和看的代码不一致。有些疑惑,不知道各位是否也遇到类似问题呢?
关于 socket.setTimeout
在高压请求下,Nodejs会出现 EventEmitter memory leak detected
的问题讨论
最近又遇到 EventEmitter memory leak detected
问题了。
背景
这个问题发生在我使用 socket.setTimeout()
方法来处理客户端连接超时时。在进行压力测试(压测)的过程中,我遇到了 #issues3 中提到的 EventEmitter
内存泄漏问题。
问题描述
socket.setTimeout()
方法用于在指定的时间后触发一个超时事件。然而,在高压请求环境下,我发现当这个超时事件被频繁触发时,Node.js 会抛出 EventEmitter memory leak detected
的警告,提示存在内存泄漏。
原因分析
经过检查 net.js
源代码,我发现理论上 socket.setTimeout()
不应该导致内存泄漏。但是实际运行中发现,当超时事件频繁触发时,Node.js 仍然报告了内存泄漏。
解决方案
为了解决这个问题,我尝试将 socket.setTimeout()
替换为 setTimeout()
。具体来说,我创建了一个独立的定时器来管理超时逻辑,而不是直接在 socket
上调用 setTimeout()
。
示例代码
const net = require('net');
// 创建一个TCP服务器
const server = net.createServer((socket) => {
console.log('Client connected');
// 使用独立的定时器来管理超时
const timeoutId = setTimeout(() => {
console.error('Socket timed out');
socket.destroy(); // 销毁socket连接
}, 5000); // 设置5秒超时
socket.on('data', (data) => {
clearTimeout(timeoutId); // 收到数据时清除定时器
console.log(`Received data: ${data.toString()}`);
socket.write('Data received');
});
socket.on('end', () => {
console.log('Client disconnected');
});
});
server.listen(8080, () => {
console.log('Server listening on port 8080');
});
解释
- 独立定时器:通过使用独立的
setTimeout()
定时器,避免了在socket
对象上直接绑定超时事件,从而减少了EventEmitter
的内存泄漏风险。 - 清除定时器:在接收到数据时清除定时器,确保只有在没有数据接收的情况下才会触发超时事件。
这种方法不仅解决了内存泄漏问题,还提高了代码的可读性和可维护性。
结论
在高压请求环境下,直接在 socket
上使用 socket.setTimeout()
可能会导致 EventEmitter
内存泄漏。通过使用独立的 setTimeout()
定时器并适时清除定时器,可以有效避免这一问题。希望这些建议对大家有所帮助。
猜测应该是 keep-alive 模式时,此socket 被多次request 所依赖,而每次request 都设置一次所致… 纯属猜测…
一开始我也怀疑是这个问题,后来我在setTimeout之前先removeAllListener(‘timeout’),结果还是一样。
请问,这个问题,解决了否???
在Node.js中,当你在高并发请求场景下使用socket.setTimeout()
时,可能会遇到EventEmitter memory leak detected
警告。这是因为socket.setTimeout()
内部维护了一个定时器,如果这些定时器没有被正确清理,就会导致内存泄漏。
示例代码
假设我们有一个简单的TCP客户端连接到服务器,并设置了超时时间:
const net = require('net');
const client = new net.Socket();
client.connect(12345, 'localhost', () => {
console.log('Connected');
});
// 使用 socket.setTimeout 设置超时
client.setTimeout(5000, () => {
console.log('Socket timeout');
client.destroy(); // 销毁socket
});
client.on('data', (data) => {
console.log('Received: ', data.toString());
});
client.on('end', () => {
console.log('Disconnected');
});
在这个例子中,如果你在高并发情况下频繁创建和销毁socket,可能会导致定时器累积,从而引发内存泄漏问题。
解决方案
为了防止这个问题,你可以使用setTimeout
来替代socket.setTimeout
。这样可以避免Node.js内部对定时器的管理问题。
const net = require('net');
const { setTimeout: setTimeoutGlobal } = require('timers/promises');
const client = new net.Socket();
client.connect(12345, 'localhost', () => {
console.log('Connected');
});
async function checkTimeout() {
await setTimeoutGlobal(5000);
console.log('Global timeout reached');
client.destroy();
}
checkTimeout().catch(console.error);
client.on('data', (data) => {
console.log('Received: ', data.toString());
});
client.on('end', () => {
console.log('Disconnected');
});
总结
通过使用全局的setTimeout
而非socket.setTimeout
,你可以避免由于定时器管理不当导致的内存泄漏问题。确保在每个请求完成后正确清理资源,也是预防此类问题的关键。