在生产环境中运行 Node.js 应用,不能简单地用 node app.js 启动就完事。一旦进程因未捕获的异常而崩溃、服务器重启,或者需要利用多核 CPU 处理高并发时,单靠原始启动命令远远不够。PM2 是 Node.js 生态中最成熟、使用最广泛的进程管理工具,它解决了进程守护、负载均衡、自动重启、日志管理和状态监控等一系列生产环境刚需,甚至还能管理多项目、部署到云端。
19.1.1 PM2 是什么,为什么需要它
PM2(Process Manager 2)是一个基于 Node.js 开发的进程管理器,通过守护进程的方式确保应用持续运行。它最常见的功能包括:
- 进程守护:应用发生致命错误退出时,自动重启。
- 集群模式:利用多核 CPU,启动多个工作进程并实现负载均衡。
- 日志管理:统一收集标准输出和错误日志,支持日志切割、实时查看。
- 零停机重启:重新加载应用而不断开现有连接。
- 进程监控:实时查看 CPU、内存占用,以及请求处理速率。
- 多项目管理:通过配置文件管理多个 Node.js 应用,统一启停。
- 启动脚本:系统重启后自动恢复所有托管进程。
对于单机部署或中小型项目,PM2 足以替代 Docker 编排之外的进程管理需求,让开发者把精力集中在业务逻辑上。
19.1.2 安装与快速上手
全局安装 PM2 后即可使用终端命令管理所有应用:
npm install -g pm2
# 或使用 yarn / pnpm
启动一个最简单的应用:
pm2 start app.js
此时 PM2 会为 app.js 分配一个进程名(默认为文件名),并让它在后台以守护模式运行。标准的输出和错误流会被 PM2 捕获并写入日志文件,不会在终端直接显示。常用命令如下:
| 命令 | 说明 |
|------|------|
| pm2 start app.js | 启动应用 |
| pm2 start app.js --name "my-api" | 指定应用名称 |
| pm2 list | 查看所有应用的状态(名称、ID、运行时间、CPU/内存) |
| pm2 stop <name\|id> | 停止应用(可指定名称或 ID) |
| pm2 restart <name\|id> | 重启应用 |
| pm2 delete <name\|id> | 从 PM2 列表中移除应用 |
| pm2 logs | 实时查看日志 |
| pm2 monit | 进入实时监控界面 |
pm2 list 的输出类似一张表格,直观展示了各进程的运行状态和资源占用,能够快速定位异常进程。
19.1.3 集群模式:利用多核 CPU
Node.js 主线程是单线程的,默认只占用一个 CPU 核心。在四核或八核服务器上,如果不启动多个进程,其余的核心就白白浪费了。PM2 的 cluster mode 可以根据 CPU 核心数自动派生多个工作进程,并在它们之间进行负载均衡。
集群模式的启动非常简单,只需加上 -i 参数:
pm2 start app.js -i max
max 表示启动与 CPU 核心数相等的工作进程。也可以指定具体数量,例如 -i 4。PM2 会创建一个主进程和多个子进程,所有子进程共享同一个端口(PM2 内部使用 Node.js 的 cluster 模块实现端口复用)。当有请求到达时,主进程通过 Round-Robin 调度算法将请求分发给健康的子进程,从而实现负载均衡。
需要注意,集群模式要求应用本身是无状态的,或者将会话数据存储到 Redis/数据库等共享存储中,否则不同请求可能被分发到不同进程上,导致状态丢失。此外,内存中的定时器、全局变量也会在每个进程中独立存在,设计逻辑时需要考虑这点。
19.1.4 自动重启与守护机制
PM2 会持续监控托管的进程,如果进程因未捕获的异常或 process.exit() 而退出,PM2 会自动重启它,默认情况下立即重启。这种机制防止了单次错误导致的服务完全挂掉。不过,如果进程在短时间内反复崩溃(例如刚启动几秒就退出),PM2 会认为应用发生了不可恢复的错误,并停止自动重启,此时需要手动排查。
可通过参数调整自动重启的行为:
--max-restarts <次数>:连续重启失败的最大次数(默认 15 次)。--min-uptime <时间>:如果进程存活短于该时间则算作非正常启动,默认 1000ms。--restart-delay <毫秒>:重启之间的延迟。
此外,内存溢出重启 是一个非常实用的生产特性。可以设置一个内存使用上限,当进程的 RSS 内存超过该阈值时,PM2 会自动重启该进程,从而避免因内存泄漏导致服务器资源耗尽:
pm2 start app.js --max-memory-restart 200M
这个参数在内存持续增长尚未触发系统 OOM Killer 时就主动释放,保证服务的整体可用性。不过这只是“治标”的手段,真正的内存泄漏仍需要通过代码排查和修复。
19.1.5 日志管理
console.log 和 console.error 在生产环境中需要被妥善收集,否则会丢失或扰乱终端。PM2 自动捕获所有托管进程的标准输出和标准错误,并写入到默认日志目录 ~/.pm2/logs/ 中,文件命名规则为 <app-name>-out.log 和 <app-name>-error.log。
常用日志操作:
- 实时查看:
pm2 logs显示所有应用的日志流,pm2 logs my-api只查看指定应用。可加--lines 100指定显示最后多少行。 - 日志切割:随着运行时间增长,日志文件会变得庞大。PM2 提供了
pm2-logrotate模块,可自动切割日志文件、保存一定时间后清理。安装后需要配置。
安装并设置:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M # 单个文件超过 10M 即切割
pm2 set pm2-logrotate:retain 30 # 保留 30 个日志文件
- 格式自定义:可以在启动时通过
--log-date-format参数为日志添加时间戳。
对于更复杂的日志需求(如结构化日志输出),可以在应用内部使用 winston、pino 等日志库,再结合 PM2 的日志功能统一管理。
19.1.6 零停机重启与优雅关闭
生产环境更新代码时,直接 pm2 restart 会导致短暂的服务中断。PM2 提供了 reload 命令实现零停机重启(Zero-downtime reload):
pm2 reload my-api
其工作原理是:先启动一个新的工作进程,等新进程就绪后,再优雅关闭旧的工作进程,整个过程不会丢失等候中的请求。要实现这一点,需要在应用代码中监听 SIGINT 或 shutdown 消息,在进程退出前优雅地关闭数据库连接、服务器监听等。
示例:在 Express 应用中监听关闭信号:
const server = app.listen(3000);
process.on('SIGINT', () => {
console.log('正在关闭服务...');
server.close(() => {
console.log('服务已关闭');
process.exit(0);
});
});
PM2 在执行 reload 时会向旧进程发送 SIGINT,如果应用能在合理时间内退出,就能实现无缝升级。
19.1.7 监控面板与状态查看
除了命令行,PM2 还提供了一个 Web 监控面板,可以通过 pm2 web 命令启动一个本地服务,以仪表盘形式展示所有进程的 CPU、内存、请求延迟等信息。对于多台服务器,PM2 Plus(官网提供的在线服务)可以集中展示,但需要注册账号并可能收费。
对于本地监控,使用 pm2 monit 可以进入实时的文本界面,通过上下键切换进程,查看每个进程的 CPU 和内存使用率,以及日志尾部内容。这对于快速诊断性能问题非常有用。
19.1.8 使用配置文件管理多项目
当一台服务器上运行多个应用或多个启动参数时,直接在命令行上管理会变得混乱。PM2 支持使用 JSON 或 JavaScript 格式的配置文件(通常命名为 ecosystem.config.js),集中定义所有应用的启动参数、环境变量、日志等。
一个典型的配置文件示例:
module.exports = {
apps: [
{
name: 'api-gateway',
script: './apps/api/main.js',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
max_memory_restart: '500M',
error_file: './logs/api-error.log',
out_file: './logs/api-out.log'
},
{
name: 'worker',
script: './apps/worker/index.js',
instances: 1,
exec_mode: 'fork',
env: {
NODE_ENV: 'production'
}
}
]
};
启动配置文件:
pm2 start ecosystem.config.js
配置文件使多项目部署变得清晰,版本控制也更加方便,而且可以通过 env 字段为不同环境(production、staging)预先定义好环境变量组。
19.1.9 开机自启与重启后的恢复
生产环境中服务器重启后,需要自动恢复所有 PM2 托管的进程。PM2 提供了生成启动脚本的功能,支持 systemd、upstart、launchd 等系统初始化系统。
生成开机启动脚本:
pm2 startup
该命令会输出类似 sudo env PATH=$PATH:/usr/bin pm2 startup systemd -u youruser --hp /home/youruser 的指令,复制运行即可。之后,当系统中的所有进程运行正常时,执行 pm2 save 保存当前列表,这样服务器重启后 PM2 就会自动恢复这些进程。
19.1.10 小结:PM2 作为单机运维的“瑞士军刀”
对于中小规模 Node.js 应用的部署,PM2 足以承担起进程守护和运维的任务。它让开发者可以:
- 用一条命令启动并管理任意数量的应用。
- 通过集群模式释放多核 CPU 的并发潜力。
- 自动重启崩溃进程,避免人工恢复。
- 集中管理日志,并设置自动切割。
- 实现零停机更新。
- 通过配置文件维护多项目,提高运维效率。
与容器编排平台(如 Kubernetes)相比,PM2 更轻量级,适合单服务器或少量主机的场景。当应用需要水平扩展至多主机时,再结合 Docker、负载均衡器和 Kubernetes 等工具即可,但即便那时,PM2 也可以在容器内部继续发挥单进程管理、日志采集的作用。理解 PM2 的核心能力并熟练使用,是每个 Node.js 开发者在部署环节的必备技能。