科技 · 实用指南
Node 报 EADDRINUSE?杀进程前先证明绑定地址、监听者与守护程序
保留完整地址与端口组合,在正确命名空间确认监听者,经真实所有者停止它,并验证一次受控重启。

这份指南把 EADDRINUSE 当作一次绑定所有权审计,而不是一条“杀掉 Node”的指令。先保留错误对象,再按地址、协议族和命名空间锁定冲突范围。
后续步骤把监听 PID 追到真正管理它的终端、守护程序、服务或容器;只有确认套接字释放后才进行一次受控重启,并用可观察结果验收。
1. 固定完整绑定失败
重启前保存第一份完整错误对象,记录 code、syscall、address、port 或 Unix 套接字路径,以及真实启动命令、工作目录、Node 版本和启动它的 nodemon、PM2、systemd、Docker 或 IDE。EADDRINUSE 只说明请求的本地地址已被占用,不会告诉你所有者。
不要把“listen EADDRINUSE: address already in use :::3000”简化成“3000 端口忙”。通配地址、IP 协议族和网络命名空间都属于冲突条件。
2. 先读地址与协议族,再看端口
Node 文档说明,省略 host 时,支持 IPv6 的系统通常绑定 ::,否则绑定 0.0.0.0。许多系统上的 IPv6 通配监听也接收 IPv4,因此同端口的第二个 IPv4 绑定仍可能冲突。
把报错地址与应用预期暴露范围比较。127.0.0.1、0.0.0.0、::1 和 :: 并不等价;除非部署设计确实要求,否则不要靠扩大绑定或关闭 IPv6 来消除报错。
3. 在同一命名空间检查监听者
Linux 可先运行 ss -ltnp,或对目标端口过滤。ss 手册中 -l 表示监听、-n 表示数字形式、-p 表示进程信息。若权限隐藏进程字段,只使用取得该信息所需的最小权限。
macOS 可用 lsof -nP -iTCP:3000 -sTCP:LISTEN;Windows 可将 Get-NetTCPConnection 与 Get-Process 配合。在容器中,应同时检查主机发布端口和实际失败的容器命名空间。
4. 从 PID 追到真实所有者
监听 PID 可能只是一次性子进程。记录命令行和父进程,判断是否由 nodemon、PM2、systemd、Docker Compose、Kubernetes 或 IDE 管理。只杀子进程可能让守护程序立刻重新占用套接字。
nodemon 的实际问题表明,保存触发重启时也可能在 :: 和 5000 上发生冲突。正确方向是校正守护生命周期与关闭流程,而不是终止机器上所有 Node 进程。
5. 经所有者停止并验证释放
使用所有者的正常停止动作:停止对应终端任务、让 nodemon 或 PM2 停止命名应用、停止 systemd 单元或相关容器。先给应用关闭 server 的机会,强制终止只应在身份和范围已证明后使用。
再次运行同一套套接字检查。退出条件不是“停止命令返回”,而是目标监听在相关命名空间消失且不会被立即拉起。避免 killall node、宽泛任务终止和照抄 sudo 命令。
6. 只有架构需要时才改端口
两个有意存在的服务需要不同端点,或部署明确动态分配端口时,改端口才正确。盲目跳端口只会留下旧守护程序,并把冲突推迟到下次启动。
若使用端口 0,应在 listening 事件后读取实际地址并传给客户端。固定端口若属于代理、防火墙、服务发现或健康检查契约,就要整体更新,而不是只改 Node 进程。
7. 只重启一次并证明监听者
先关闭 reload 与多进程,只启动一个实例。确认 PID 或容器、准确本地地址、端口、协议族和一个已知健康路由,再恢复守护程序并验证旧监听会先关闭。
若冲突复发,保存新错误并与首次记录比较所有者、父进程、命名空间和时间线。清理后的套接字仍反复冲突通常指向重复启动或重启竞态,而不是缓存或 Node 安装损坏。
