Node.js嵌套使用WebSocket出现EventEmitter内存泄漏问题排查
问题原因
你的泄漏问题完全是编码逻辑不规范导致的,和WebSocket堆叠使用没有关系,问题集中在wsServer的connection回调内的冗余注册逻辑:
- 你将全局单例的Twitch聊天客户端
client的PRIVMSG事件监听放在了连接回调里,每有一个新客户端连接你的本地WebSocket服务,就会给client新增一个独立的PRIVMSG监听器,Node.js的EventEmitter默认监听器数量阈值是10,当连接数超过10就会触发泄漏警告。 - 你还在连接回调里重复注册了
process.uncaughtException全局异常监听器、重复执行client.connect()和client.join("channel")逻辑,这部分属于完全冗余的错误操作。 - 更严重的是这些绑定的监听器不会随着本地WebSocket连接断开而自动销毁,还会持有已经失效的
socket对象引用,不仅会触发警告,还会造成真实的内存泄漏,甚至在给已断开连接的socket发消息时抛出异常。
修复方案
- 将Twitch客户端的事件监听、连接、加频道逻辑全部移到全局作用域,仅执行一次
- 维护一个存活的本地WebSocket连接池,收到Twitch消息时遍历连接池转发消息
- 本地WebSocket连接断开时,及时从连接池移除对应的实例,避免残留引用
修复后代码如下:
const { paintballShot } = require('./JavaScript/paintballGunFire'); const { readPin } = require('./JavaScript/readPin'); const ws = require('ws'); const express = require('express'); const app = express(); // 维护存活的本地WebSocket连接池 const connectedSockets = new Set(); //CONNECT TO TWITCH let client = new ChatClient({ connection: { type: "websocket", secure: true, } }); // Twitch事件监听全部移到全局,只注册一次 client.on("ready", () => console.log("Successfully connected to chat")); client.on("close", (error) => { if (error != null) { console.error("Client closed due to error", error); } }); // PRIVMSG监听全局只注册一次 client.on("PRIVMSG", (msg, error) => { const sendContent = JSON.stringify(`${msg.displayName}: ${msg.messageText}`); // 遍历所有存活的连接转发消息 for (const socket of connectedSockets) { if (socket.readyState === ws.OPEN) { if (["right", "left", "fire"].includes(msg.messageText)) { socket.send(sendContent); } } } if (msg.messageText === "fire") { paintballShot(); } if (msg.messageText === "pin") { readPin(); } }); // Twitch连接、加频道逻辑全局只执行一次 client.connect(); client.join("channel"); //create headless websocket const wsServer = new ws.Server({ noServer: true }); wsServer.on('connection', function connection(socket) { // 新连接加入连接池 connectedSockets.add(socket); socket.on('message', message => console.log(message)); // 连接断开时从池子里移除 socket.on('close', () => { connectedSockets.delete(socket); }); }); // `server` is a vanilla Node.js HTTP server const server = app.listen(3000); server.on('upgrade', (request, socket, head) => { wsServer.handleUpgrade(request, socket, head, socket => { wsServer.emit('connection', socket, request); }); }); process.on('uncaughtException', function (err) { console.log(err); });
内容的提问来源于stack exchange,提问作者J. Doe
相关产品推荐
相关产品推荐

