Ubuntu Server 20.04下Node.js+PM2集群模式启动异常排查
我之前在Ubuntu 20.04上也碰到过一模一样的问题,直接用node server.js正常,但PM2集群模式就不停重启报EADDRINUSE,后来排查下来是几个常见原因,给你几个可行的解决方案:
Ubuntu 20.04自带的Node.js版本和18.04的默认版本有差异,PM2的集群模式对某些Node版本的端口监听逻辑处理有变化。先看看你当前的Node版本:
node -v
如果是v14及以上,大概率是你的服务代码没有适配集群模式的端口共享机制——默认情况下,PM2集群模式会让主进程创建监听句柄,子进程复用它,但如果你的代码里每个进程都自己去绑定3000端口,就会冲突。
如果你的server.js里是直接写app.listen(3000),那在集群模式下每个子进程都会尝试绑定3000端口,必然报错。可以改成两种方式:
方式一:让PM2自动处理集群逻辑(推荐)
把你的Express app导出,只在直接运行时才启动监听:
const express = require('express'); const app = express(); // 你的路由和中间件逻辑 app.get('/', (req, res) => { res.send('API is running'); }); // 只有当直接执行server.js时才启动监听 if (!module.parent) { app.listen(3000, () => { console.log(`Server running on port 3000`); }); } // 导出app供PM2集群模式复用 module.exports = app;
这样PM2启动集群时,会自动创建共享的监听句柄,子进程不需要自己绑定端口。
方式二:手动处理集群逻辑
如果你想自己控制集群行为,可以用Node.js内置的cluster模块:
const cluster = require('cluster'); const os = require('os'); const app = require('./your-app'); // 假设你的app在单独文件里 const http = require('http'); const numCPUs = os.cpus().length; if (cluster.isPrimary) { console.log(`主进程 ${process.pid} 启动`); // 衍生对应CPU核心数的子进程 for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker) => { console.log(`子进程 ${worker.process.pid} 退出,重新启动...`); cluster.fork(); }); } else { // 子进程复用主进程的监听句柄 http.createServer(app).listen(3000); console.log(`子进程 ${process.pid} 启动,监听端口3000`); }
旧版本PM2和Ubuntu 20.04的Node.js可能存在兼容性问题,先更新PM2:
npm install pm2@latest -g pm2 update
更新后先删掉旧的PM2进程,再重新启动:
pm2 delete server pm2 start server.js --name server -i max
虽然你确认没有其他Node服务,但可能存在僵尸进程占用3000端口,用以下命令检查:
sudo lsof -i :3000
或者:
sudo ss -tulpn | grep :3000
如果发现有进程占用,直接杀掉:
sudo kill -9 <进程PID>
然后再启动PM2。
Ubuntu 20.04默认优先IPv6,可能导致Node.js尝试绑定IPv6的::地址时出现冲突。可以强制服务只监听IPv4:
// 在listen时指定0.0.0.0 app.listen(3000, '0.0.0.0', () => { console.log(`Server listening on 0.0.0.0:3000`); });
或者启动PM2时设置环境变量强制IPv4优先:
PM2_NODE_OPTIONS="--dns-result-order=ipv4first" pm2 start server.js --name server -i max
内容的提问来源于stack exchange,提问作者Satyam

