Node.js 报 EADDRINUSE(Address already in use)怎么办?
EADDRINUSE 表示端口被占用。解决:lsof -i :端口号 找到进程,kill 掉;或换端口。也常因"服务已在后台运行"或进程未优雅退出导致。本文给出各系统排查命令与预防措施。
端口被别的进程占着。找到它、结束它,或者让你的服务换个端口。
第一步:找到占用端口的进程
# Linux / macOS
lsof -i :3000
# 输出示例:node 12345 user ... TCP *:3000 (LISTEN)
# 那个 12345 就是 PID
Windows:
netstat -ano | findstr :3000
:: 最后一列是 PID
第二步:结束进程
kill -9 12345 # Linux/macOS 强制结束
taskkill /PID 12345 /F :: Windows
-9(SIGKILL)是强杀,进程没机会清理。温和点先用 kill <PID>(SIGTERM),不听话再 -9。
第三步:重启服务
进程释放端口后重新启动服务,确认监听的是预期进程,再请求一个业务接口。只确认端口有监听还不够:占用该端口的可能是旧实例或另一个服务。
常见诱因与预防
- 上次的进程没退干净:Ctrl+C 没真正杀死(比如 nodemon 子进程残留)。养成
kill掉残留 node 进程的习惯:pkill -f "node app.js"; - 服务已在后台/另一个终端运行:先
lsof看一眼再启动; - TIME_WAIT 残留:刚关闭的服务立刻重启偶发占用,Node 监听默认处理得当,一般等几十秒自愈;
- 代码里 listen 了两次:检查是否重复调用
app.listen()。
代码层防御——监听失败时给出友好提示:
const server = app.listen(3000);
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error('端口 3000 被占用,请检查是否有残留进程');
process.exit(1);
}
throw err;
});
常见问题(FAQ)
Q: kill 了马上重启还是 EADDRINUSE?
可能杀错进程(lsof 再看一次),或该服务由 supervisor/systemd 守护被自动拉起——先停守护服务。
Q:能不能让 Node 自动换个端口?
可以:监听 error 事件捕获 EADDRINUSE 后 server.listen(port + 1) 重试。开发环境方便,生产环境建议固定端口并报错。
Q:Docker 里报这个错?
多半是容器内端口映射冲突或同容器跑了两个监听进程,检查 docker ps 的端口映射与 Dockerfile 的启动命令。