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 还是换端口,不要简单改端口掩盖僵尸进程。
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/服务名
- 标出最近发布、配置、证书、镜像或依赖变更
lsof -iTCP:3000 -sTCP:LISTEN -n -P
ss -lntp | grep :30002. 在故障发生的同一位置复现
很多误判来自测试位置不同:本机通不代表服务器通,容器内通不代表宿主机通,集群外通也不代表 Pod 内通。复现命令要尽量贴近真实流量路径。
- 从报错服务所在机器执行测试
- 必要时加 Host、SNI、namespace 或代理参数
- 把成功路径和失败路径的输出并排比较
ss -lntp | grep :3000
kill -9 <pid>
fuser -k 3000/tcp3. 读取服务端证据,而不是只看客户端提示
客户端报错通常是结果摘要。真正能区分根因的是服务端日志、内核日志、事件、进程状态或数据库状态。
- 确认占用目标端口的进程 PID。
- 判断是自己的旧进程还是其他服务。
- 旧进程用 kill 终止,注意确认不是误杀其他服务。
kill -9 <pid>
fuser -k 3000/tcp
PORT=3001 node server.js4. 做最小修复并立即回归验证
每次只改一个变量,修复后用同一条复现命令回测,并观察日志是否停止增长。这样可以避免多个临时改动互相掩盖。
- 旧进程用 kill 终止,注意确认不是误杀其他服务。
- 其他服务则换端口或调整部署架构。
- 排查是否有僵尸进程未正确退出,避免简单改端口掩盖问题。
fuser -k 3000/tcp
PORT=3001 node server.jsCommands
排查命令与使用意图
命令 1
用于确认进程打开了哪些文件、socket 或资源限制。适合定位端口占用、deleted 文件和文件句柄耗尽。
lsof -iTCP:3000 -sTCP:LISTEN -n -P命令 2
用于确认端口监听状态和连接关系。重点看 LISTEN 行的地址是 127.0.0.1 还是 0.0.0.0,以及 ESTAB 连接数。
ss -lntp | grep :3000命令 3
用于补充“Node.js EADDRINUSE 端口被占用排查”的排查证据链。kill 的输出需要结合上下文其他命令交叉验证,不要单独依赖某一条结果下结论。
kill -9 <pid>命令 4
用于补充“Node.js EADDRINUSE 端口被占用排查”的排查证据链。fuser 的输出需要结合上下文其他命令交叉验证,不要单独依赖某一条结果下结论。
fuser -k 3000/tcp命令 5
用于补充“Node.js EADDRINUSE 端口被占用排查”的排查证据链。PORT=3001 的输出需要结合上下文其他命令交叉验证,不要单独依赖某一条结果下结论。
PORT=3001 node server.jsRisk 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 端口占用排查
Need More Help
这个问题还没完全解决?
把你的报错、环境和已经尝试过的步骤发给我,后续会优先补充到排障手册。