frontend Troubleshooting

Node.js EADDRINUSE 端口被占用排查

Node.js 启动服务报 Error: listen EADDRINUSE: address already in use :::3000 时,用 lsof -iTCP:3000 -sTCP:LISTEN 或 ss -lntp 找到占用进程 PID,判断是自己的僵尸进程还是其他服务,再决定 kill 释放端口还是通过 PORT 环境变量换端口。

EADDRINUSE 表示端口已被其他进程占用。先找到占用进程再决定 kill 还是换端口,不要简单改端口掩盖僵尸进程。

适用环境Node.js / Nuxt / Vite / CI
风险等级
最后验证2026-07-03
建议权限普通用户

Scenario

这篇文章解决什么问题

适用于 Node.js 启动时报 Error: listen EADDRINUSE: address already in use :::3000 的场景。排查核心是找到占用端口的进程并判断是僵尸进程还是其他服务,而不是简单换端口。

Symptoms

典型现象

  • Node.js 启动报 Error: listen EADDRINUSE: address already in use :::3000。
  • pm2 list 显示有残留进程占用端口。
  • kill 进程后端口仍被占用(TIME_WAIT 状态或子进程未退出)。
  • 开发环境频繁遇到,通常是 nodemon/webpack-dev-server 没正确退出。

Before Debugging

排查前先确认

  • 先区分浏览器运行时、构建期、SSR/水合、依赖安装和网络 API 问题。
  • 保留浏览器控制台、Network 失败请求、构建日志和 lockfile 状态。
  • 确认本地、CI、预览环境和生产环境的 Node 版本与环境变量是否一致。

Root Causes

常见根因分层

运行环境差异

Node 版本、包管理器、lockfile、CI 内存、浏览器 API 与 SSR 环境差异都会导致“本地好、线上坏”。

  • 固定 Node/pnpm 版本
  • CI 使用 frozen lockfile
  • 构建后 preview 复现

客户端和服务端首屏不一致

时间、随机数、localStorage、window、viewport 条件渲染会引发 hydration mismatch 或闪烁。

  • SSR 阶段输出确定内容
  • 浏览器专属逻辑放 mounted/ClientOnly
  • 避免首屏按 viewport 分支

浏览器安全策略和跨域

CORS、Cookie SameSite、HTTPS、混合内容和缓存策略会让接口在 Postman 可用但浏览器失败。

  • 看预检 OPTIONS
  • 确认凭证模式与 Allow-Origin
  • 从 Network 读取真实响应

Diagnosis Path

分步诊断流程

1. 固定现场,避免追着变化状态跑

先记录故障时间、影响入口、完整错误和最近一次变更。Node.js EADDRINUSE 端口被占用排查 这类问题经常在重启、重试或缓存刷新后短暂消失,没有现场信息会很难复盘。

  • 截图或保存完整错误文本
  • 记录请求域名/IP/端口/namespace/服务名
  • 标出最近发布、配置、证书、镜像或依赖变更
1. 固定现场,避免追着变化状态跑
lsof -iTCP:3000 -sTCP:LISTEN -n -P
ss -lntp | grep :3000

2. 在故障发生的同一位置复现

很多误判来自测试位置不同:本机通不代表服务器通,容器内通不代表宿主机通,集群外通也不代表 Pod 内通。复现命令要尽量贴近真实流量路径。

  • 从报错服务所在机器执行测试
  • 必要时加 Host、SNI、namespace 或代理参数
  • 把成功路径和失败路径的输出并排比较
2. 在故障发生的同一位置复现
ss -lntp | grep :3000
kill -9 <pid>
fuser -k 3000/tcp

3. 读取服务端证据,而不是只看客户端提示

客户端报错通常是结果摘要。真正能区分根因的是服务端日志、内核日志、事件、进程状态或数据库状态。

  • 确认占用目标端口的进程 PID。
  • 判断是自己的旧进程还是其他服务。
  • 旧进程用 kill 终止,注意确认不是误杀其他服务。
3. 读取服务端证据,而不是只看客户端提示
kill -9 <pid>
fuser -k 3000/tcp
PORT=3001 node server.js

4. 做最小修复并立即回归验证

每次只改一个变量,修复后用同一条复现命令回测,并观察日志是否停止增长。这样可以避免多个临时改动互相掩盖。

  • 旧进程用 kill 终止,注意确认不是误杀其他服务。
  • 其他服务则换端口或调整部署架构。
  • 排查是否有僵尸进程未正确退出,避免简单改端口掩盖问题。
4. 做最小修复并立即回归验证
fuser -k 3000/tcp
PORT=3001 node server.js

Commands

排查命令与使用意图

命令 1

用于确认进程打开了哪些文件、socket 或资源限制。适合定位端口占用、deleted 文件和文件句柄耗尽。

命令 1
lsof -iTCP:3000 -sTCP:LISTEN -n -P

命令 2

用于确认端口监听状态和连接关系。重点看 LISTEN 行的地址是 127.0.0.1 还是 0.0.0.0,以及 ESTAB 连接数。

命令 2
ss -lntp | grep :3000

命令 3

用于补充“Node.js EADDRINUSE 端口被占用排查”的排查证据链。kill 的输出需要结合上下文其他命令交叉验证,不要单独依赖某一条结果下结论。

命令 3
kill -9 <pid>

命令 4

用于补充“Node.js EADDRINUSE 端口被占用排查”的排查证据链。fuser 的输出需要结合上下文其他命令交叉验证,不要单独依赖某一条结果下结论。

命令 4
fuser -k 3000/tcp

命令 5

用于补充“Node.js EADDRINUSE 端口被占用排查”的排查证据链。PORT=3001 的输出需要结合上下文其他命令交叉验证,不要单独依赖某一条结果下结论。

命令 5
PORT=3001 node server.js

Risk Notes

容易踩坑的地方

  • 不要简单改端口掩盖僵尸进程问题,长期累积会占用大量端口和内存。
  • kill -9 不给进程清理机会,可能导致数据库连接泄漏或临时文件残留,优先用 kill(SIGTERM)。
  • Docker 容器内端口被占用时,可能是上一个容器没正确停止,docker ps -a 查看残留容器。

Verification

修复后怎么确认

  • 原始故障现象已经消失,且使用同一入口重新测试 Node.js EADDRINUSE 端口被占用 能稳定成功。
  • 服务端日志不再出现同类错误,监控中的错误率、重启次数、连接数或延迟回到正常区间。
  • 如果做过临时绕过,已经记录恢复计划,例如还原宽松策略、补监控、补限制或提交正式配置。

Prevention

后续预防

  • 开发环境用 PORT 环境变量管理端口,配合 cross-env 跨平台设置。
  • PM2 管理的进程用 pm2 kill 清理所有进程,而不是手动 kill。
  • 在应用中监听 SIGTERM/SIGINT 信号,正确关闭服务器和数据库连接后退出。

常见问题

为什么 kill 后端口还占用?

可能进程处于 TIME_WAIT 状态或未真正退出。用 ss 或 lsof 确认进程是否已终止。

PM2 管理的进程怎么查?

用 pm2 list 查看进程列表,pm2 logs 查看日志,pm2 restart 或 pm2 delete 管理进程。

Long-tail SEO

你可能正在搜索这些报错

下面这些错误原文、场景词和中英文搜索词,都是排查同类问题时常见的入口。

  • Node.js EADDRINUSE
  • node 端口被占用
  • lsof 查端口占用
  • EADDRINUSE address already in use
  • node kill 进程 端口
  • ss 查看端口 linux
  • node 僵尸进程 端口占用
  • pm2 端口占用排查