Express应用中Nodemon重启报EADDRINUSE错误求助
我之前碰到过好多次这种情况,核心问题就是Nodemon重启时,你的Express旧进程没有彻底退出,还死死占着3001端口,导致新进程启动时端口被占用报错。下面给你一步步的解决方法:
临时应急:手动清理占用端口的进程
你已经用lsof -i :3001查到了端口占用的进程,直接杀掉它就行:
- 从
lsof的输出里找到对应的PID(进程ID) - 执行命令彻底杀掉进程:
kill -9 <你的PID>
比如PID是1234,就运行kill -9 1234,之后再重启Nodemon,应该就能正常启动了。不过这只是临时解决,要根治得往下看。
根治方案:确保旧进程彻底退出
方案1:给Express应用添加信号监听,优雅关闭
在你的app/app.js里,需要把Express的服务器实例存下来,然后监听SIGTERM信号(Nodemon重启时会发送这个信号给旧进程),让服务器优雅关闭:
const express = require('express'); const app = express(); // 你的路由和业务代码... // 把服务器实例赋值给变量,不要直接app.listen(3001) const server = app.listen(3001, () => { console.log('服务器运行在端口3001'); }); // 监听SIGTERM信号,优雅关闭服务器 process.on('SIGTERM', () => { console.log('收到重启信号,正在关闭服务器...'); server.close(() => { console.log('服务器已关闭,旧进程退出'); process.exit(0); // 确保进程彻底退出 }); });
这样Nodemon重启时,旧进程会先关闭服务器,释放端口,再退出,就不会和新进程冲突了。
方案2:配置Nodemon发送正确信号并增加延迟
如果不想改代码,可以直接修改Nodemon的启动参数,让它发送SIGTERM信号,同时给旧进程留一点关闭时间:
修改package.json里的scripts:
"scripts": { "start:dev": "nodemon --signal SIGTERM --delay 1000 app/app.js" }
--delay 1000是给旧进程1秒的时间关闭端口,避免新进程启动太快抢端口。
你也可以创建一个nodemon.json配置文件,把配置写进去,这样scripts更简洁:
{ "watch": ["app"], "signal": "SIGTERM", "delay": 1000, "exec": "node app/app.js" }
然后scripts改成:
"scripts": { "start:dev": "nodemon" }
方案3:检查是否有未关闭的长连接
如果上面两种方法都没用,那可能是你的应用里有其他长连接(比如数据库连接、WebSocket、Redis连接)没关闭,导致旧进程没法正常退出。这时候需要在SIGTERM的监听里,同时关闭这些连接:
比如如果用了MongoDB:
process.on('SIGTERM', () => { server.close(() => { // 关闭数据库连接 mongoClient.close(() => { console.log('数据库连接已关闭'); process.exit(0); }); }); });
确保所有占用资源的连接都被关闭,旧进程才能彻底退出,释放端口。
额外排查:确认端口占用的来源
如果手动杀进程后还是报错,可能是系统的端口缓存问题,你可以临时把Express的端口改成3002,看看能不能启动,如果能,就说明还是旧进程残留的问题,再回到上面的方案排查。
内容的提问来源于stack exchange,提问作者hhoburg

