EC2自动部署重启时Node.js集群fork报EADDRINUSE错误排查
问题分析与解决思路
可能的原因
- 主/子进程重复监听端口:如果主进程代码中也存在监听端口的逻辑,或者子进程的监听配置强制独占端口,会导致多个进程争抢同一端口触发
EADDRINUSE。 - 端口共享配置缺失:Node.js Cluster多进程共享端口依赖系统的
SO_REUSEPORT特性,若未显式开启(Node.js v10.12.0+支持reusePort参数),EC2实例的系统环境可能默认限制了端口共享。 - 部署脚本逻辑问题:即使是全新实例,自动部署脚本可能存在重复执行启动命令的情况,导致多个主进程同时fork子进程,引发端口冲突。
- 子进程启动竞争:EC2实例启动时网络服务未完全就绪,子进程绑定端口的时机过早,触发暂时的端口占用错误。
排查与解决方法
1. 修正Cluster代码逻辑
确保主进程仅负责管理子进程,不监听端口;子进程监听端口时显式开启端口共享:
const cluster = require('cluster'); const os = require('os'); const http = require('http'); // Node.js v16+ 使用 isPrimary,旧版本用 isMaster if (cluster.isPrimary) { const numCPUs = os.cpus().length; for (let i = 0; i < numCPUs; i++) { cluster.fork(); } // 子进程异常退出时自动重启 cluster.on('exit', (worker) => { console.log(`子进程 ${worker.process.pid} 退出,重启中`); cluster.fork(); }); } else { const server = http.createServer((req, res) => { res.writeHead(200); res.end('服务响应\n'); }); // 显式开启 reusePort 确保多进程共享端口 server.listen(3000, { reusePort: true }, () => { console.log(`子进程 ${process.pid} 监听端口3000`); }); // 捕获端口占用错误,添加重试逻辑 server.on('error', (err) => { if (err.code === 'EADDRINUSE') { console.error(`进程 ${process.pid} 端口3000被占用,1秒后重试`); setTimeout(() => { server.close(); server.listen(3000, { reusePort: true }); }, 1000); } else { throw err; } }); }
2. 检查部署脚本
- 确保部署脚本仅执行一次启动命令,避免重复启动主进程。
- 启动前可添加进程检查命令(如
pkill -f node),清理可能残留的Node进程(即使是全新实例,也可能因脚本重试导致进程残留)。
3. 验证系统端口共享配置
在EC2实例上执行以下命令,确认SO_REUSEPORT相关配置是否开启:
sysctl net.ipv4.tcp_tw_reuse net.ipv4.tcp_tw_recycle
若返回值为0,可临时开启:
sysctl -w net.ipv4.tcp_tw_reuse=1 sysctl -w net.ipv4.tcp_tw_recycle=1
如需永久生效,可编辑/etc/sysctl.conf添加对应配置后执行sysctl -p。
内容的提问来源于stack exchange,提问作者Abhay Gupta
相关产品推荐
相关产品推荐

