必须复用单例Connection并显式关闭:全局只调用一次amqp.connect(),channel使用后调close(),connection关闭需await且监听error/close事件重连,进程退出时按channel→connection顺序await关闭。

amqplib 重复 new Connection 会直接耗尽系统文件句柄
Node.js 进程默认能打开的文件句柄数有限(Linux 通常为 1024),而每个 amqp.connect() 调用都会创建至少一个 TCP socket、若干内部流对象和事件监听器。在循环中反复调用,不 close 就丢弃,句柄不会自动回收——不是“慢释放”,是根本不会释放,直到进程被 OS 杀掉(EMFILE 错误)。
如何识别是不是 Connection 泄漏而非其他资源问题
别猜,用 lsof -p $PID | grep tcp 或 netstat -anp | grep $PID 直接看连接数是否随请求量线性增长;再配合 process._getActiveHandles() 输出,如果大量 TCPWrap 或 Socket 对象持续存在,基本锁定是 Connection 没关。
- 错误日志里出现
connect EADDRINUSE或connect EMFILE是最直接信号 - AMQP 连接建立后没调用
connection.close(),哪怕只建连不发消息,也会卡住句柄 - 使用
amqp.connect()返回的 Promise 未 await 就进入下一次循环,容易导致多个 pending 连接堆积
修复必须满足三个硬条件:单例 + 显式 close + 错误兜底
Connection 对象开销大,不能按请求新建,必须复用。但复用不等于“永不关闭”——它需要可恢复的生命周期管理。
-
amqp.connect()应全局只调用一次,返回的connection存在模块级变量或依赖注入容器中 - 所有通道(
channel)必须从该单例connection创建,并在使用完毕后显式调用channel.close() - Connection 本身需监听
error和close事件,在异常时触发重连逻辑,而不是静默丢弃旧实例 - 绝不能在
try/catch中捕获连接错误后继续用已断开的connection—— 它的状态不可信
示例关键逻辑:
let connection = null;
async function getConnection() {
if (connection && connection.isConnected) return connection;
try {
connection = await amqp.connect('amqp://...');
connection.on('error', handleConnectionError);
connection.on('close', handleConnectionClose);
return connection;
} catch (err) {
console.error('AMQP connect failed:', err);
throw err;
}
}
最容易被忽略的坑:connection.close() 不是同步完成的
connection.close() 返回 Promise,但很多开发者以为调了就完事。实际它只是发送 AMQP 关闭帧,等待 broker 确认后才 resolve。如果在 close 未完成时又发起新 connect,两个连接可能并存,句柄照样涨。
- 务必
await connection.close()再执行后续逻辑 - 在服务退出(
process.on("SIGTERM"))时,必须 await 所有活跃 channel.close() 和 connection.close() - 不要用
connection.destroy()替代close()—— 前者是暴力中断,broker 端状态不同步,下次重连可能被拒绝
真正的清理链是:channel.close() → await → connection.close() → await → 进程退出。中间任何一环跳过,泄漏就发生了。


















