{T}

PM2 详细配置指南与示例

概述

什么是 PM2?

PM2(Process Manager 2)是一个功能强大的 Node.js 进程管理器,提供生产级别的进程管理、负载均衡、日志管理、监控和部署功能。

核心特性

特性描述
进程守护应用崩溃后自动重启,保证服务高可用
负载均衡内置 Cluster 模式,充分利用多核 CPU
日志管理统一的日志收集、轮转和查询
监控面板实时监控 CPU、内存等资源使用
零停机部署支持平滑重载,服务不中断
多环境管理开发、测试、生产环境配置隔离

适用场景

  • 生产环境部署:保证 Node.js 应用稳定运行
  • 微服务架构:管理多个独立服务进程
  • 开发环境:文件监听自动重启,提升开发效率
  • CI/CD 流程:集成到自动化部署流水线

系统架构

架构图

code
┌─────────────────────────────────────────────────────────────────┐
│                         PM2 架构                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐        │
│  │   PM2 CLI   │    │  PM2 API    │    │ PM2 Plus    │        │
│  │  (命令行)    │    │  (编程接口)  │    │ (监控面板)   │        │
│  └──────┬──────┘    └──────┬──────┘    └──────┬──────┘        │
│         │                  │                  │                │
│         └──────────────────┼──────────────────┘                │
│                            ▼                                    │
│                  ┌─────────────────┐                            │
│                  │   PM2 Daemon    │                            │
│                  │   (守护进程)     │                            │
│                  └────────┬────────┘                            │
│                           │                                     │
│         ┌─────────────────┼─────────────────┐                  │
│         ▼                 ▼                 ▼                  │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐          │
│  │   God       │   │   Satan     │   │   God       │          │
│  │  (App #1)   │   │  (App #2)   │   │  (App #N)   │          │
│  │  Cluster    │   │   Fork      │   │  Cluster    │          │
│  └──────┬──────┘   └──────┬──────┘   └──────┬──────┘          │
│         │                 │                 │                  │
│         ▼                 ▼                 ▼                  │
│  ┌─────────────┐   ┌─────────────┐   ┌─────────────┐          │
│  │  Worker 1   │   │   Worker    │   │  Worker 1   │          │
│  │  Worker 2   │   │   (单进程)   │   │  Worker 2   │          │
│  │  ...        │   │             │   │  ...        │          │
│  └─────────────┘   └─────────────┘   └─────────────┘          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

执行模式对比

code
Fork 模式                    Cluster 模式
┌─────────┐                 ┌─────────┐
│ Master  │                 │ Master  │
│ Process │                 │ Process │
└────┬────┘                 └────┬────┘
     │                           │
     ▼                      ┌────┼────┐
┌─────────┐                 ▼    ▼    ▼
│ Worker  │             ┌────┐┌────┐┌────┐
│ (单进程) │             │ W1 ││ W2 ││ W3 │
└─────────┘             └────┘└────┘└────┘

适用场景:                  适用场景:
- 定时任务                  - HTTP 服务
- 消息队列消费              - WebSocket 服务
- 单线程应用                - CPU 密集型任务

工作流程

code
启动流程:
┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐
│  CLI   │───▶│ Daemon │───▶│ Spawn  │───▶│ Running│
│ 命令   │    │ 初始化  │    │ 进程   │    │ 运行   │
└────────┘    └────────┘    └────────┘    └────────┘

重启流程:
┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐
│ Reload │───▶│ Graceful│───▶│ New    │───▶│ Old    │
│  命令  │    │  Spawn  │    │ Worker │    │ Worker │
└────────┘    └────────┘    └────────┘    │  Exit  │
                                          └────────┘

安装与初始化

安装 PM2

bash
# 全局安装(推荐)
npm install pm2 -g

# 使用 yarn
yarn global add pm2

# 验证安装
pm2 --version

初始化配置文件

bash
# 生成示例配置文件
pm2 ecosystem

# 或手动创建
touch ecosystem.config.js

核心功能模块

1. 进程守护

PM2 的核心功能是进程守护,确保应用在崩溃后自动重启。

code
进程状态流转:
                                    
  ┌─────────┐    start    ┌──────────┐
  │ stopped │────────────▶│ launching│
  └─────────┘             └────┬─────┘
       ▲                       │
       │                       ▼
       │                 ┌──────────┐
       │    stop         │  online  │◀─────┐
       └─────────────────│          │      │
                         └────┬─────┘      │
                              │            │
                       crash  │     restart│
                              ▼            │
                         ┌──────────┐      │
                         │ errored  │──────┘
                         └──────────┘

守护机制:

  • 监听进程退出事件
  • 根据重启策略决定是否重启
  • 记录重启次数和原因
  • 支持最大重启次数限制

2. 负载均衡(Cluster 模式)

利用 Node.js 的 Cluster 模块实现多进程负载均衡:

javascript
// Cluster 模式配置
{
  instances: 'max',        // 启动与 CPU 核心数相同的进程
  exec_mode: 'cluster'     // 启用集群模式
}

负载均衡原理:

code
                Master Process
                       │
       ┌───────────────┼───────────────┐
       │               │               │
       ▼               ▼               ▼
   ┌───────┐       ┌───────┐       ┌───────┐
   │Worker1│       │Worker2│       │Worker3│
   │ :3000 │       │ :3000 │       │ :3000 │
   └───────┘       └───────┘       └───────┘
       │               │               │
       └───────────────┴───────────────┘
                       │
                       ▼
              共享同一个端口
              (通过 IPC 通信)

3. 日志管理

PM2 提供完善的日志管理功能:

bash
# 日志文件类型
├── combined.log    # 所有日志(标准输出 + 错误)
├── out.log         # 标准输出日志
└── error.log       # 错误日志

日志配置选项:

参数类型说明
log_fileString合并日志文件路径
out_fileString标准输出日志路径
error_fileString错误日志路径
log_date_formatString时间戳格式
merge_logsBoolean合并集群模式日志
log_typeString日志格式(json/raw)

4. 监控功能

bash
# 实时监控面板
pm2 monit

监控指标:

指标说明
CPUCPU 使用率 (%)
Memory内存占用 (MB)
Uptime运行时长
Restarts重启次数
Status进程状态

PM2 Plus 监控面板:

code
┌────────────────────────────────────────┐
│           PM2 Plus Dashboard           │
├────────────────────────────────────────┤
│  App Name    CPU    Memory   Status    │
│  ─────────────────────────────────────│
│  api-server  12%    256MB    online    │
│  web-app     8%     128MB    online    │
│  worker      5%     64MB     online    │
├────────────────────────────────────────┤
│  Total: 3 apps | 25% CPU | 448MB RAM   │
└────────────────────────────────────────┘

5. 环境变量管理

支持多环境配置隔离:

javascript
// 环境配置优先级
env_production > env_staging > env > 默认值

// 环境切换
pm2 start app.js --env production
pm2 start app.js --env staging

6. 零停机重载

code
Graceful Reload 流程:

时间线 ─────────────────────────────────────▶

旧进程: ████████████████░░░░░░░░░░░░░░░░░░停止
              │          │
新进程:      ░░░░░░░░░░░░████████████████████运行
              │          │
              │          └── 旧进程处理完请求后退出
              └── 新进程启动,开始接收请求

过渡期间请求不丢失

配置参数详解

配置参数速查表

基础配置

参数类型默认值说明
nameString-应用名称(必填)
scriptString-入口文件路径(必填)
cwdString-工作目录
argsString/Array-命令行参数
interpreterString'node'解释器路径
interpreter_argsString/Array-解释器参数
node_argsString/Array-Node.js 参数(别名)

实例配置

参数类型默认值说明
instancesNumber/'max'1实例数量
exec_modeString'fork'执行模式:fork/cluster
pid_fileString-PID 文件路径
cron_restartString-定时重启(cron 格式)

日志配置

参数类型默认值说明
log_fileString-合并日志文件
out_fileString-标准输出日志
error_fileString-错误日志
log_date_formatString-时间戳格式
timeBooleanfalse启用时间戳
merge_logsBooleanfalse合并集群日志
disable_logsBooleanfalse禁用日志

重启策略

参数类型默认值说明
autorestartBooleantrue崩溃后自动重启
watchBoolean/Stringfalse监听文件变化
ignore_watchArray[]忽略监听的文件
max_restartsNumber16最大重启次数
min_uptimeString1000ms最小运行时间
restart_delayNumber0重启延迟(毫秒)
max_memory_restartString-内存限制重启

高级配置

参数类型默认值说明
source_map_supportBooleantrue支持 Source Map
instance_varString'NODE_APP_INSTANCE'实例 ID 变量名
listen_timeoutNumber3000启动超时(毫秒)
kill_timeoutNumber1600停止超时(毫秒)
wait_readyBooleanfalse等待 ready 信号
shutdown_with_messageBooleanfalse关闭时发送消息

配置文件示例

1. 最简单的配置文件 (ecosystem.config.js)

javascript
module.exports = {
  apps: [{
    name: 'my-app',           // 应用名称
    script: './app.js',       // 入口文件
    instances: 1,             // 实例数量
    exec_mode: 'fork',        // 执行模式: fork 或 cluster
    watch: false,             // 是否监听文件变化
    env: {
      NODE_ENV: 'development', // 开发环境变量
      PORT: 3000
    },
    env_production: {
      NODE_ENV: 'production', // 生产环境变量
      PORT: 80
    }
  }]
};

2. 完整的配置文件示例

javascript
module.exports = {
  apps: [
    {
      name: 'api-server',
      script: './dist/server.js',
      
      // 实例配置
      instances: 'max',        // 使用最大CPU核心数
      exec_mode: 'cluster',    // 集群模式
      
      // 日志配置
      log_file: './logs/combined.log',
      out_file: './logs/out.log',
      error_file: './logs/error.log',
      log_date_format: 'YYYY-MM-DD HH:mm Z',
      time: true,
      
      // 性能监控
      max_memory_restart: '1G', // 内存超过1G时重启
      node_args: '--max-old-space-size=1024',
      
      // 自动重启
      watch: false,
      ignore_watch: [
        'node_modules',
        'logs',
        'uploads'
      ],
      
      // 重启策略
      min_uptime: '10s',
      max_restarts: 10,
      restart_delay: 4000,
      autorestart: true,
      
      // 环境变量
      env: {
        NODE_ENV: 'development',
        PORT: 3000,
        DEBUG: 'app:*'
      },
      env_staging: {
        NODE_ENV: 'staging',
        PORT: 4000
      },
      env_production: {
        NODE_ENV: 'production',
        PORT: 80,
        INSTANCE_VAR: 'production_value'
      },
      
      // 高级配置
      source_map_support: true,
      instance_var: 'INSTANCE_ID'
    }
  ],

  // 部署配置
  deploy: {
    production: {
      user: 'ubuntu',
      host: ['server1.example.com', 'server2.example.com'],
      ref: 'origin/main',
      repo: 'git@github.com:user/repo.git',
      path: '/var/www/app',
      'pre-deploy': 'git fetch --all',
      'post-deploy': 'npm install && pm2 reload ecosystem.config.js --env production',
      'pre-setup': 'echo "开始服务器设置"'
    },
    staging: {
      user: 'ubuntu',
      host: 'staging.example.com',
      ref: 'origin/develop',
      repo: 'git@github.com:user/repo.git',
      path: '/var/www/staging',
      'post-deploy': 'npm install && pm2 start ecosystem.config.js --env staging'
    }
  }
};

常用配置项详解

实例管理配置

javascript
{
  instances: 4,                    // 固定4个实例
  // instances: 'max',             // 根据CPU核心数自动设置
  exec_mode: 'cluster',           // 集群模式
  pid_file: '/var/run/app.pid'    // PID文件路径
}

日志配置详解

javascript
{
  // 日志文件配置
  log_file: './logs/combined.log',     // 所有日志
  out_file: './logs/app-out.log',      // 标准输出日志
  error_file: './logs/app-err.log',    // 错误日志
  log_date_format: 'YYYY-MM-DD HH:mm:ss.SSS',
  time: true,                          // 显示时间戳
  
  // 日志轮转
  log_type: 'json',                    // JSON格式日志
  merge_logs: true,                    // 合并集群日志
  disable_logs: false                  // 禁用日志
}

监控和自动重启配置

javascript
{
  // 资源监控
  max_memory_restart: '500M',          // 内存限制
  max_restarts: 10,                    // 最大重启次数
  min_uptime: '10s',                   // 最小运行时间
  
  // 重启策略
  restart_delay: 4000,                 // 重启延迟
  autorestart: true,                   // 自动重启
  cron_restart: '0 3 * * *',           // 定时重启(每天3点)
  
  // 健康检查
  listen_timeout: 3000,               // 启动超时时间
  kill_timeout: 5000                  // 停止超时时间
}

文件监听配置

javascript
{
  watch: true,                         // 启用文件监听
  watch_delay: 1000,                   // 监听延迟
  ignore_watch: [                      // 忽略的文件/目录
    'node_modules',
    'logs',
    '*.log',
    'test',
    '.git'
  ],
  watch_options: {                    // 监听选项
    followSymlinks: false,
    usePolling: true,
    interval: 1000
  }
}

多应用配置示例

javascript
module.exports = {
  apps: [
    // API 服务器
    {
      name: 'api-service',
      script: './services/api/index.js',
      instances: 2,
      exec_mode: 'cluster',
      env: {
        PORT: 3001,
        SERVICE_NAME: 'api'
      }
    },
    
    // WebSocket 服务
    {
      name: 'websocket-service',
      script: './services/websocket/server.js',
      instances: 1,
      env: {
        PORT: 3002,
        SERVICE_NAME: 'websocket'
      }
    },
    
    // 定时任务服务
    {
      name: 'cron-service',
      script: './services/cron/index.js',
      instances: 1,
      exec_mode: 'fork',
      autorestart: false,              // 定时任务不需要自动重启
      env: {
        SERVICE_NAME: 'cron'
      }
    },
    
    // 静态文件服务
    {
      name: 'static-service',
      script: './services/static/server.js',
      instances: 1,
      env: {
        PORT: 3003,
        SERVICE_NAME: 'static'
      }
    }
  ]
};

环境特定配置

javascript
module.exports = {
  apps: [{
    name: 'my-app',
    script: './app.js',
    
    // 开发环境
    env_development: {
      NODE_ENV: 'development',
      PORT: 3000,
      DATABASE_URL: 'mongodb://localhost:27017/dev',
      LOG_LEVEL: 'debug',
      CORS_ORIGIN: 'http://localhost:3000'
    },
    
    // 测试环境
    env_test: {
      NODE_ENV: 'test',
      PORT: 3001,
      DATABASE_URL: 'mongodb://localhost:27017/test',
      LOG_LEVEL: 'info'
    },
    
    // 生产环境
    env_production: {
      NODE_ENV: 'production',
      PORT: 80,
      DATABASE_URL: 'mongodb://prod-db:27017/app',
      LOG_LEVEL: 'warn',
      CORS_ORIGIN: 'https://myapp.com'
    }
  }]
};

部署配置详解

javascript
module.exports = {
  apps: [...], // 应用配置
  
  deploy: {
    // 生产环境部署
    production: {
      key: '/path/to/ssh/key.pem',          // SSH密钥
      user: 'deploy',                       // 服务器用户
      host: [                               // 服务器列表
        { host: 'server1.example.com', port: 22 },
        { host: 'server2.example.com', port: 2222 }
      ],
      ssh_options: 'StrictHostKeyChecking=no',
      ref: 'origin/main',                   // Git分支
      repo: 'git@github.com:user/repo.git', // 仓库地址
      path: '/home/deploy/app/production',  // 部署路径
      
      // 部署钩子
      'pre-deploy': 'echo "开始部署"',
      'pre-deploy-local': 'npm run test',   // 本地预部署检查
      'post-deploy': `
        npm install --production
        npm run build
        pm2 reload ecosystem.config.js --env production
        echo "部署完成"
      `,
      'pre-setup': 'sudo apt-get update',   // 服务器初始化
      'post-setup': 'echo "服务器设置完成"'
    },
    
    // 蓝绿部署配置
    production_blue_green: {
      user: 'deploy',
      host: 'server.example.com',
      ref: 'origin/main',
      repo: 'git@github.com:user/repo.git',
      path: '/home/deploy/app',
      
      // 蓝绿部署特定配置
      'post-deploy': `
        if [ -d "current" ]; then
          cp current/.env ./source/.env
          mv current previous
        fi
        ln -nfs ./source current
        cd current
        npm install
        pm2 startOrReload ecosystem.config.js --env production
        
        # 健康检查
        sleep 10
        if curl -f http://localhost:3000/health; then
          echo "部署成功"
          rm -rf previous
        else
          echo "部署失败,回滚"
          pm2 reload previous/ecosystem.config.js --env production
          exit 1
        fi
      `
    }
  }
};

使用示例

1. 启动应用

bash
# 使用配置文件启动
pm2 start ecosystem.config.js

# 指定环境启动
pm2 start ecosystem.config.js --env production

# 启动单个应用(多应用配置时)
pm2 start ecosystem.config.js --only api-service

2. 部署命令

bash
# 初始化部署(首次)
pm2 deploy ecosystem.config.js production setup

# 部署更新
pm2 deploy ecosystem.config.js production

# 回滚到上一个版本
pm2 deploy ecosystem.config.js production revert 1

3. 进程管理

bash
# 查看进程列表
pm2 list

# 监控资源使用
pm2 monit

# 查看日志
pm2 logs
pm2 logs api-service --lines 100

# 重新启动
pm2 reload all
pm2 reload api-service

# 停止应用
pm2 stop all
pm2 delete api-service

命令行使用

命令速查表

进程管理

命令说明
pm2 start <file|config|json>启动应用
pm2 stop <id|name|all>停止应用
pm2 restart <id|name|all>重启应用
pm2 reload <id|name|all>平滑重载(零停机)
pm2 delete <id|name|all>删除应用
pm2 kill停止所有应用并关闭守护进程

信息查看

命令说明
pm2 list查看所有应用列表
pm2 status同 list
pm2 show <id|name>查看应用详情
pm2 describe <id|name>同 show
pm2 monit打开监控面板
pm2 prettylist美化输出应用列表

日志管理

命令说明
pm2 logs查看所有日志
pm2 logs <id|name>查看指定应用日志
pm2 logs --lines <n>显示最后 n 行
pm2 flush清空所有日志
pm2 reloadLogs重载日志文件

其他命令

命令说明
pm2 startup生成开机自启动脚本
pm2 unstartup取消开机自启动
pm2 save保存当前进程列表
pm2 resurrect恢复保存的进程列表
pm2 reset <id|name|all>重置重启计数器
pm2 update更新 PM2 内存中的进程
pm2 ping检查 PM2 守护进程状态

常用命令示例

bash
# 启动应用
pm2 start app.js                      # 基本启动
pm2 start app.js --name my-app        # 指定应用名称
pm2 start app.js -i max               # 启动最大 CPU 核心数个实例
pm2 start app.js -i 4 --cluster       # 启动 4 个集群实例
pm2 start app.js --watch              # 启用文件监听
pm2 start app.js --env production     # 指定环境

# 停止应用
pm2 stop all                          # 停止所有应用
pm2 stop 0                            # 停止 ID 为 0 的应用
pm2 stop my-app                       # 停止指定名称的应用

# 重启/重载
pm2 restart all                       # 重启所有应用
pm2 reload all                        # 平滑重载所有应用
pm2 gracefulReload all                # 优雅重载

# 删除应用
pm2 delete all                        # 删除所有应用
pm2 delete my-app                     # 删除指定应用

# 查看信息
pm2 list                              # 查看列表
pm2 show my-app                       # 查看详情
pm2 monit                             # 监控面板

# 日志操作
pm2 logs                              # 查看所有日志
pm2 logs my-app --lines 200           # 查看最后 200 行
pm2 flush                             # 清空日志

# 开机自启动
pm2 startup                           # 生成启动脚本
pm2 save                              # 保存当前状态
pm2 unstartup                         # 取消自启动

# 部署相关
pm2 deploy ecosystem.config.js production setup
pm2 deploy ecosystem.config.js production
pm2 deploy ecosystem.config.js production revert 1

命令选项详解

bash
pm2 start <file> [options]

# 实例选项
--name <name>              # 应用名称
-i, --instances <n>        # 实例数量(max 表示 CPU 核心数)
--no-vizion                # 禁用版本控制
--no-autorestart           # 禁用自动重启

# 执行选项
--exec-mode <mode>         # 执行模式(fork/cluster)
--interpreter <path>       # 解释器路径
--node-args "<args>"       # Node.js 参数
-- <args>                  # 应用参数

# 日志选项
--log <path>               # 日志文件路径
--output <path>            # 标准输出日志
--error <path>             # 错误日志
--time                     # 添加时间戳
--log-date-format <fmt>    # 时间戳格式
--no-daemon                # 前台运行

# 监听选项
--watch                    # 启用文件监听
--ignore-watch <paths>     # 忽略监听的文件
--watch-delay <ms>         # 监听延迟

# 重启选项
--max-memory-restart <mem> # 内存限制重启
--max-restarts <n>         # 最大重启次数
--min-uptime <ms>          # 最小运行时间
--restart-delay <ms>       # 重启延迟
--cron <pattern>           # 定时重启

# 环境选项
--env <name>               # 指定环境变量配置

编程 API 接口

基本用法

PM2 提供了完整的编程 API,可以在 Node.js 代码中管理进程。

javascript
const pm2 = require('pm2');

// 连接到 PM2 守护进程
pm2.connect((err) => {
  if (err) {
    console.error(err);
    process.exit(2);
  }
  
  // 连接成功后的操作...
});

核心 API 方法

进程启动

javascript
// 启动应用
pm2.start({
  name: 'my-app',
  script: './app.js',
  instances: 2,
  exec_mode: 'cluster'
}, (err, apps) => {
  if (err) throw err;
  console.log('Started:', apps);
  pm2.disconnect(); // 断开连接
});

// 启动配置文件中的应用
pm2.start('ecosystem.config.js', (err, proc) => {
  if (err) throw err;
  console.log('Started from config');
});

进程停止与重启

javascript
// 停止进程
pm2.stop('my-app', (err, proc) => {
  if (err) throw err;
  console.log('Stopped');
});

// 重启进程
pm2.restart('my-app', (err, proc) => {
  if (err) throw err;
  console.log('Restarted');
});

// 平滑重载
pm2.reload('my-app', (err, proc) => {
  if (err) throw err;
  console.log('Reloaded');
});

// 删除进程
pm2.delete('my-app', (err, proc) => {
  if (err) throw err;
  console.log('Deleted');
});

进程查询

javascript
// 获取所有进程列表
pm2.list((err, list) => {
  if (err) throw err;
  console.log(list);
  // list 是进程描述对象数组
});

// 查找特定进程
pm2.describe('my-app', (err, desc) => {
  if (err) throw err;
  console.log(desc);
});

// 查询单个进程详情
pm2.describe(0, (err, desc) => {
  console.log(desc[0].monit); // CPU 和内存信息
});

日志操作

nginx
// 刷新日志
pm2.flush((err) => {
  if (err) throw err;
  console.log('Logs flushed');
});

// 重载日志
pm2.reloadLogs((err) => {
  if (err) throw err;
  console.log('Logs reloaded');
});

Promise 封装

javascript
const pm2 = require('pm2');
const { promisify } = require('util');

// Promisify PM2 方法
const connect = promisify(pm2.connect.bind(pm2));
const start = promisify(pm2.start.bind(pm2));
const stop = promisify(pm2.stop.bind(pm2));
const restart = promisify(pm2.restart.bind(pm2));
const list = promisify(pm2.list.bind(pm2));
const disconnect = promisify(pm2.disconnect.bind(pm2));

// 使用 async/await
async function manageApp() {
  try {
    await connect();
    
    // 启动应用
    await start({
      name: 'my-app',
      script: './app.js'
    });
    
    // 获取列表
    const procs = await list();
    console.log('Running processes:', procs.length);
    
    // 重启
    await restart('my-app');
    
  } catch (err) {
    console.error('Error:', err);
  } finally {
    await disconnect();
  }
}

manageApp();

事件监听

javascript
// 监听进程事件
pm2.launchBus((err, bus) => {
  if (err) throw err;
  
  // 监听日志
  bus.on('log:out', (data) => {
    console.log('App:', data.process.name);
    console.log('Output:', data.data);
  });
  
  // 监听错误
  bus.on('log:err', (data) => {
    console.error('Error from:', data.process.name);
    console.error('Message:', data.data);
  });
  
  // 监听进程事件
  bus.on('process:event', (data) => {
    console.log('Event:', data.event); // online, exit, restart...
    console.log('Process:', data.process.name);
  });
});

完整示例

javascript
const pm2 = require('pm2');

class PM2Manager {
  constructor() {
    this.connected = false;
  }
  
  async connect() {
    return new Promise((resolve, reject) => {
      pm2.connect((err) => {
        if (err) reject(err);
        else {
          this.connected = true;
          resolve();
        }
      });
    });
  }
  
  async startApp(config) {
    return new Promise((resolve, reject) => {
      pm2.start(config, (err, proc) => {
        if (err) reject(err);
        else resolve(proc);
      });
    });
  }
  
  async stopApp(name) {
    return new Promise((resolve, reject) => {
      pm2.stop(name, (err, proc) => {
        if (err) reject(err);
        else resolve(proc);
      });
    });
  }
  
  async listApps() {
    return new Promise((resolve, reject) => {
      pm2.list((err, list) => {
        if (err) reject(err);
        else resolve(list);
      });
    });
  }
  
  async getAppStatus(name) {
    const list = await this.listApps();
    return list.find(p => p.name === name);
  }
  
  disconnect() {
    pm2.disconnect();
    this.connected = false;
  }
}

// 使用示例
async function main() {
  const manager = new PM2Manager();
  
  try {
    await manager.connect();
    
    // 启动应用
    await manager.startApp({
      name: 'api-server',
      script: './server.js',
      instances: 2,
      exec_mode: 'cluster',
      env: { PORT: 3000 }
    });
    
    // 查看状态
    const apps = await manager.listApps();
    console.log('Running apps:', apps.map(a => ({
      name: a.name,
      status: a.pm2_env.status,
      cpu: a.monit.cpu,
      memory: a.monit.memory
    })));
    
  } finally {
    manager.disconnect();
  }
}

main().catch(console.error);

性能优化建议

1. 实例数量选择

javascript
// CPU 密集型任务:等于 CPU 核心数
instances: 'max'

// I/O 密集型任务:可以超过 CPU 核心数
instances: 'max',  // 或具体数值如 4、8

// 内存受限场景:根据内存预算计算
// 可用内存 / 单进程内存 = 实例数
instances: Math.floor(totalMemory / perProcessMemory)

2. 内存管理

javascript
{
  // 设置内存限制
  max_memory_restart: '800M',
  
  // Node.js 堆内存限制
  node_args: '--max-old-space-size=1024',
  
  // 其他优化参数
  env_production: {
    UV_THREADPOOL_SIZE: 64,              // 增加线程池大小
    NODE_OPTIONS: '--max-http-header-size=16384'
  }
}

3. 日志优化

javascript
{
  // 避免日志文件过大
  log_file: '/var/log/app/combined.log',
  out_file: '/dev/null',    // 禁用标准输出日志
  error_file: '/var/log/app/error.log',
  
  // 使用 JSON 格式便于分析
  log_type: 'json',
  log_date_format: 'YYYY-MM-DD HH:mm:ss.SSS'
}

4. 启动优化

javascript
{
  // 预热启动
  wait_ready: true,
  listen_timeout: 10000,  // 给应用足够的启动时间
  
  // 应用代码中发送 ready 信号
  // process.send('ready');
}

5. Graceful Shutdown

javascript
// 应用代码中实现优雅关闭
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);

async function gracefulShutdown() {
  console.log('Received shutdown signal...');
  
  // 1. 停止接收新请求
  server.close();
  
  // 2. 处理完现有请求
  await closeDatabaseConnections();
  await flushQueues();
  
  // 3. 退出进程
  process.exit(0);
}

6. 性能监控集成

javascript
// 集成 PM2 Plus
{
  name: 'my-app',
  script: 'app.js',
  
  // PM2 Plus 配置
  instance_var: 'NODE_APP_INSTANCE',
  
  // 自定义指标
  // 在应用代码中:
  // const io = require('@pm2/io');
  // io.init({
  //   metrics: {
  //     network: true,
  //     http: true,
  //     v8: true
  //   }
  // });
}

故障排查指南

常见问题诊断流程

code
应用异常
    │
    ├── 检查状态: pm2 list
    │
    ├── 查看日志: pm2 logs
    │
    ├── 检查详情: pm2 show <name>
    │
    └── 监控面板: pm2 monit

问题排查清单

1. 应用无法启动

bash
# 检查错误日志
pm2 logs my-app --err

# 查看完整信息
pm2 show my-app

# 常见原因:
# - 脚本路径错误
# - 依赖未安装
# - 端口被占用
# - 权限不足

2. 应用频繁重启

bash
# 查看重启次数
pm2 list

# 检查是否触发作内存限制
pm2 show my-app | grep "restart_time\|memory_limit"

# 解决方案:
# - 检查内存泄漏
# - 调整 max_memory_restart
# - 增加 min_uptime

3. 高 CPU 使用

bash
# 使用监控面板
pm2 monit

# 生成 CPU Profile
pm2 profile:cpu 10s cpu-profile.cpuprofile

# 解决方案:
# - 检查代码中的死循环
# - 优化同步操作
# - 增加实例数

4. 内存泄漏

bash
# 生成堆快照
pm2 profile:mem memory.heapsnapshot

# 监控内存趋势
pm2 show my-app

# 解决方案:
# - 分析堆快照
# - 设置合理的 max_memory_restart
# - 定期重启 (cron_restart)

5. 日志文件过大

bash
# 查看日志大小
ls -lh ~/.pm2/logs/

# 清空日志
pm2 flush

# 解决方案:
# - 使用 pm2-logrotate 模块
# - 禁用不需要的日志
# - 配置日志轮转

6. 开机自启动失败

bash
# 重新生成启动脚本
pm2 unstartup
pm2 startup

# 保存当前状态
pm2 save

# 验证服务状态
systemctl status pm2-<user>

调试技巧

bash
# 以调试模式启动
pm2 start app.js --node-args="--inspect=9229"

# 查看完整进程信息
pm2 describe my-app

# 实时监控
pm2 monit

# 检查 PM2 守护进程状态
pm2 ping

常见问题解答

Q1: Fork 模式和 Cluster 模式有什么区别?

Fork 模式:

  • 单进程运行
  • 不支持端口共享
  • 适合定时任务、队列消费
  • 每个实例监听不同端口

Cluster 模式:

  • 多进程运行
  • 支持端口共享(负载均衡)
  • 适合 HTTP/WebSocket 服务
  • 充分利用多核 CPU

Q2: 如何选择实例数量?

场景建议配置
HTTP API 服务instances: 'max'
WebSocket 服务instances: 1 或少量实例
定时任务instances: 1 (Fork 模式)
消息队列消费instances: <队列分区数>
内存受限根据可用内存计算

Q3: reload 和 restart 有什么区别?

bash
# restart: 立即重启,会有短暂服务中断
pm2 restart my-app

# reload: 平滑重载,零停机
pm2 reload my-app

reload 采用滚动重启,先启动新进程再停止旧进程,适合生产环境。

Q4: 如何实现零停机部署?

  1. 使用 pm2 reload 而非 restart
  2. 应用实现 Graceful Shutdown
  3. 配置合理的 listen_timeoutkill_timeout
javascript
// 应用代码
process.on('SIGTERM', async () => {
  server.close();
  await cleanup();
  process.exit(0);
});

Q5: PM2 配置文件中环境变量如何优先?

环境变量优先级(从高到低):

  1. --env <name> 指定的环境配置
  2. env 基础环境变量
  3. 系统环境变量
javascript
// 使用指定环境
pm2 start --env production  // 使用 env_production

// 合并环境变量
// env_production 会覆盖 env 中的同名变量

Q6: 如何限制应用内存使用?

javascript
{
  // 内存超限自动重启
  max_memory_restart: '500M',
  
  // Node.js 堆内存限制
  node_args: '--max-old-space-size=400'
}

Q7: 如何实现定时重启?

javascript
{
  // 每天凌晨 3 点重启
  cron_restart: '0 3 * * *',
  
  // 每小时重启
  cron_restart: '0 * * * *'
}

Q8: PM2 日志如何轮转?

安装 pm2-logrotate 模块:

bash
pm2 install pm2-logrotate

# 配置
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7

Q9: 如何在 Docker 中使用 PM2?

dockerfile
# Dockerfile
FROM node:18-alpine

WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .

# 安装 PM2
RUN npm install pm2 -g

# 使用 PM2 启动
CMD ["pm2-runtime", "ecosystem.config.js", "--env", "production"]

注意:Docker 中使用 pm2-runtime 而非 pm2 start

Q10: 如何监控多个服务器的 PM2 应用?

使用 PM2 Plus:

bash
# 安装 PM2 Plus agent
pm2 install pm2-server-monit

# 链接到 PM2 Plus
pm2 link <secret_key> <public_key>

访问 https://app.pm2.io 查看监控面板。


最佳实践配置

javascript
module.exports = {
  apps: [{
    name: 'production-app',
    script: './dist/app.js',
    
    // 生产环境最佳配置
    instances: 'max',              // 充分利用CPU
    exec_mode: 'cluster',          // 集群模式提高性能
    
    // 资源限制防止内存泄漏
    max_memory_restart: '800M',
    node_args: '--max-old-space-size=1024',
    
    // 稳定的重启策略
    max_restarts: 5,
    min_uptime: '30s',
    restart_delay: 5000,
    
    // 完整的日志管理
    log_file: '/var/log/app/combined.log',
    out_file: '/var/log/app/out.log',
    error_file: '/var/log/app/error.log',
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
    merge_logs: true,
    
    // 生产环境变量
    env_production: {
      NODE_ENV: 'production',
      PORT: 8080,
      UV_THREADPOOL_SIZE: 64,
      NODE_OPTIONS: '--max-http-header-size=16384'
    }
  }]
};

参考资源


Node.js 22+ 部署新特性

原生 --watch 替代 PM2 的 watch 模式

bash
# 传统 PM2 watch 模式
pm2 start app.js --watch

# Node.js 22+ 原生 watch(开发环境)
node --watch app.js

# 生产环境仍推荐 PM2
pm2 start ecosystem.config.js

Node.js 22+ SEA(Single Executable Applications)

Node.js 22+ SEA 功能更加稳定,可将应用打包为单个可执行文件:

bash
# 1. 创建入口文件
echo "console.log('Hello SEA!')" > sea.js

# 2. 生成 blob
node --experimental-sea-config sea-config.json

# 3. 复制 Node.js 二进制
cp $(which node) hello

# 4. 注入 blob
npx postject hello NODE_SEA_BLOB sea-prep.blob \
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
json
// sea-config.json
{
  "main": "sea.js",
  "output": "sea-prep.blob",
  "disableExperimentalSEAWarning": true
}

权限模型增强生产安全

bash
# 生产环境启用权限模型
node --permission \
  --allow-fs-read=/app/data \
  --allow-fs-write=/app/logs \
  --allow-fs-write=/app/uploads \
  --allow-net=0.0.0.0:3000 \
  --allow-net=db.internal:5432 \
  app.js

--env-file 替代 dotenv

bash
# 传统方式
npm install dotenv
# require('dotenv').config

# Node.js 22+ 原生方式
node --env-file=.env.production app.js

# PM2 中使用
pm2 start app.js --node-args="--env-file=.env.production"