1. 为什么需要PM2?Node.js进程管理的痛点
第一次部署Node.js应用时,我天真地以为用node app.js启动服务就万事大吉了。直到凌晨3点被报警短信吵醒——服务又挂了。这是我经历的第七次崩溃,前六次我都不得不手动重新启动。这种场景正是PM2要解决的核心问题。
Node.js作为单线程运行时,一个未捕获的异常就会导致整个进程崩溃。即便用try-catch包裹了所有代码,内存泄漏、第三方库崩溃等问题仍难以避免。更糟糕的是,当我们需要运行多个实例做负载均衡时,手动管理多个进程简直是一场噩梦。
PM2的守护进程模式(Daemon)会持续监控你的应用状态,崩溃时自动重启。它还能:
- 保存应用列表,服务器重启后自动恢复
- 监控内存和CPU使用情况
- 无缝重启应用而不中断服务(零秒重启)
- 集中管理集群模式下的多个实例
实际案例:某电商网站在大促期间,使用PM2集群模式运行了8个Node.js实例。其中一个实例因异常图片处理崩溃后,PM2在200毫秒内完成了重启,用户完全无感知。
2. 环境准备与PM2安装
2.1 Node.js环境检查
在安装PM2前,建议先确认Node.js环境:
node -v # 推荐v16.x或更高版本 npm -v # 6.x+如果尚未安装Node.js,可以通过以下方式获取:
- 官方安装包(推荐): Node.js官网
- 版本管理工具(适合多项目):
# 使用nvm(Mac/Linux) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash nvm install 18 # Windows用户可用nvm-windows
2.2 PM2的三种安装方式
根据使用场景选择最适合的安装方式:
全局安装(最常用):
npm install pm2 -g安装后验证:
pm2 --version项目本地安装(适合容器化部署):
npm install pm2 --save-dev使用时需通过npx调用:
npx pm2 start app.js直接下载二进制文件(无Node环境时):
wget https://github.com/Unitech/pm2/archive/master.zip -O pm2.zip unzip pm2.zip cd pm2-master npm install # 即使没有全局Node也可运行
避坑提示:在Docker中使用时,务必避免将PM2作为主进程(PID 1),这会导致信号处理异常。正确做法是用
--init参数或tini作为init系统。
3. PM2核心功能实战
3.1 基础进程管理
启动一个简单的Express应用:
pm2 start app.js --name "my-api"常用操作命令:
pm2 list # 查看所有进程状态 pm2 logs my-api # 实时日志 pm2 stop my-api # 停止应用 pm2 restart my-api # 重启应用 pm2 delete my-api # 从列表中移除参数进阶用法:
# 设置环境变量 pm2 start app.js --env production # 传递Node参数 pm2 start app.js --node-args="--max-old-space-size=4096" # 指定工作目录 pm2 start app.js --cwd /path/to/app3.2 集群模式与负载均衡
启动集群(假设是4核CPU):
pm2 start app.js -i 4PM2会自动使用Node.js的cluster模块创建多个实例,并内置了轮询负载均衡。动态调整实例数:
pm2 scale my-api +2 # 增加2个实例 pm2 scale my-api 1 # 缩减到1个实例性能实测:在4核8G服务器上,用ApacheBench测试单实例vs集群模式:
ab -c 100 -n 5000 http://localhost:3000/结果显示集群模式的吞吐量提升了3.8倍,平均延迟降低62%。
3.3 高级功能配置
3.3.1 配置文件(推荐方案)
创建ecosystem.config.js:
module.exports = { apps: [{ name: "app-prod", script: "./app.js", instances: "max", // 根据CPU核心数自动扩展 exec_mode: "cluster", // 集群模式 autorestart: true, // 崩溃自动重启 watch: false, // 生产环境建议关闭文件监听 max_memory_restart: "1G", // 内存超过1G时重启 env: { NODE_ENV: "development", }, env_production: { NODE_ENV: "production", } }] }启动时指定环境:
pm2 start ecosystem.config.js --env production3.3.2 日志管理
默认日志路径:
- 普通日志:
~/.pm2/logs/app-out.log - 错误日志:
~/.pm2/logs/app-error.log
高级日志配置:
{ log_date_format: "YYYY-MM-DD HH:mm Z", out_file: "/var/log/my-app.stdout.log", // 自定义路径 error_file: "/var/log/my-app.stderr.log", merge_logs: true, // 集群模式合并日志 time: true // 日志添加时间戳 }日志轮转(需要安装pm2-logrotate):
pm2 install pm2-logrotate pm2 set pm2-logrotate:max_size 10M # 单个日志文件最大10MB pm2 set pm2-logrotate:retain 30 # 保留30个备份4. 生产环境最佳实践
4.1 开机自启动配置
不同系统的设置方式:
Linux (systemd):
pm2 startup systemd -u nodeuser --hp /home/nodeuser pm2 save # 保存当前进程列表Windows:
- 用管理员权限打开CMD
- 执行:
pm2-startup install pm2 save
MacOS (launchd):
pm2 startup launchd -u nodeuser --hp /Users/nodeuser pm2 save常见问题:如果修改了启动脚本,需要先
pm2 unstartup再重新设置。
4.2 监控与性能调优
内置监控面板:
pm2 monit关键指标说明:
- CPU%:超过80%可能需要优化或扩容
- 内存:V8引擎内存限制约1.7GB(64位系统)
- 循环延迟:超过100ms可能有事件循环阻塞
性能优化建议:
- 使用
--max-old-space-size调整内存限制pm2 start app.js --node-args="--max-old-space-size=4096" - 对于CPU密集型任务,使用
worker_threads - 用
pm2 logs --lines 1000分析历史错误
4.3 与Docker的集成方案
推荐的多阶段Dockerfile:
FROM node:18 as builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18-alpine RUN npm install pm2 -g WORKDIR /app COPY --from=builder /app . EXPOSE 3000 CMD ["pm2-runtime", "ecosystem.config.js"] # 专为容器设计的运行模式关键注意事项:
- 不要用
pm2 start直接作为CMD,应该用pm2-runtime - 在Kubernetes中,每个Pod只运行一个PM2实例
- 设置合理的资源限制:
# k8s deployment示例 resources: limits: cpu: "2" memory: "1Gi"
5. 故障排查与调试技巧
5.1 常见问题解决方案
问题1:应用不断重启可能原因:
- 内存超出限制(检查
max_memory_restart) - 端口冲突(用
lsof -i :3000检查) - 启动超时(增加
listen_timeout参数)
问题2:PM2命令无响应尝试:
pm2 kill # 强制终止所有进程 pm2 resurrect # 恢复之前保存的进程问题3:日志文件过大解决方案:
pm2 install pm2-logrotate pm2 set pm2-logrotate:compress true # 启用gzip压缩5.2 高级调试技巧
检查事件循环延迟:
const lag = require('event-loop-lag')(1000); setInterval(() => { console.log(`Event loop lag: ${lag()}ms`); }, 1000);生成性能分析报告:
pm2 profile 30 # 30秒CPU分析内存快照对比:
pm2 trigger my-app heapdump # 第一次快照 # 执行一些操作后 pm2 trigger my-app heapdump # 第二次快照
5.3 替代方案对比
| 工具 | 守护进程 | 集群模式 | 日志管理 | 监控面板 | 学习曲线 |
|---|---|---|---|---|---|
| PM2 | ✔️ | ✔️ | ✔️ | ✔️ | 低 |
| Forever | ✔️ | ❌ | 基本 | ❌ | 很低 |
| systemd | ✔️ | ❌ | 强大 | ❌ | 中 |
| Kubernetes | ✔️ | ✔️ | 强大 | ✔️ | 高 |
选择建议:
- 简单项目:PM2或Forever
- 微服务架构:Kubernetes+PM2组合
- 非Node应用:考虑systemd或supervisord
6. 从入门到精通的学习路径
根据我五年Node.js生产环境经验,建议这样进阶:
新手阶段(1周):
- 掌握基础启停、日志查看
- 学会使用配置文件
- 实现开机自启动
中级阶段(2-4周):
- 熟练使用集群模式
- 配置日志轮转
- 集成到CI/CD流程
高级阶段(1-3月):
- 编写自定义插件
- 源码分析(推荐研究
God进程机制) - 性能调优实战
推荐学习资源:
- 官方文档: PM2官网
- 高级技巧: PM2 Runtime Guide
- 源码分析: PM2 GitHub
最后分享一个真实案例:某SAAS平台通过合理配置PM2集群参数,在硬件不变的情况下,QPS从1200提升到2100。关键配置是:
{ instances: "max", exec_mode: "cluster", max_memory_restart: "800M", node_args: "--optimize-for-size" }