Skip to main content

PM2 重启与状态外部化

你的观察非常敏锐,答案是肯定的:当 Node.js 进程崩溃或被 PM2 强制重启时,所有存储在进程内存中的数据都会丢失。

这是由 Node.js 的运行机制决定的——每个进程都拥有独立且互不共享的内存空间。进程一旦终止,其占用的内存就会被操作系统回收,其中的数据也随之消失。

不过,这并非无解的问题。下面我们来深入探讨 PM2 在这种情况下的行为,以及正确的应对之道。

PM2 的不同“重启”模式

PM2 提供了几种不同的重启方式,它们对内存数据的处理逻辑是一样的(都会清空),但对服务可用性的影响天差地别。

命令行为服务停机时间内存数据
pm2 restart立即同时杀死所有进程,然后再启动新进程。存在(服务会短暂不可用)全部丢失
pm2 reload逐个重启进程,始终保证有至少一个进程在运行。零停机(Zero-downtime)全部丢失(但服务不中断)
pm2 start with --max-memory-restart当进程内存占用超过设定阈值时,PM2 会自动执行重启以释放内存。取决于配置(restart有停机,reload无停机)全部丢失

可以看到,无论哪种方式,只要进程重启,内存数据就保不住。PM2 解决的是 “服务不中断” 的问题,而不是 “数据不丢失” 的问题。

核心解决方案:将“状态”外移

既然进程内存不可靠,正确的做法是将需要持久化或共享的数据移出进程内存,存储在一个独立于 Node.js 进程生命周期的外部系统中。

1. 使用外部存储(最佳实践)

  • 内存数据库/缓存(如 Redis):适合存储 Session、高频访问的缓存数据等。Redis 本身将数据保存在内存中,读写极快,且可以配置数据持久化到磁盘,重启后数据不丢失。
  • 关系型数据库(如 PostgreSQL)或 NoSQL 数据库(如 MongoDB):适合存储需要持久化的核心业务数据。
  • 文件系统:对于简单的配置或数据,也可以直接写入文件。

2. 实现“优雅关闭”(Graceful Shutdown)

这是为了在 PM2 主动重启(如部署或手动 reload)时,最大限度地保存当前状态和完成正在处理的请求,实现“无损”重启。

你需要在代码中监听 PM2 发出的 SIGINT 信号:

process.on('SIGINT', async () => {
console.log('收到 SIGINT 信号,准备优雅关闭...');

// 1. 停止接收新请求
server.close(async (err) => {
if (err) {
console.error('关闭服务器时出错:', err);
process.exit(1);
}

// 2. 处理完所有进行中的请求和任务
// 例如:等待所有数据库查询完成,或处理完队列中的任务

// 3. 将当前内存中的重要状态保存到外部存储(如 Redis 或数据库)
await saveCurrentStateToRedis();

// 4. 关闭数据库连接等外部资源
await dbClient.close();

// 5. 进程退出
console.log('优雅关闭完成,进程退出。');
process.exit(0);
});

// 设置超时,防止进程永远挂起(PM2 默认 1600ms 后强制杀死)
setTimeout(() => {
console.error('优雅关闭超时,强制退出。');
process.exit(1);
}, 1500);
});

这样,在 PM2 执行 reload 时,旧进程在被杀死前,有机会完成手头的工作并将关键状态保存下来。

3. 应对“意外崩溃”

对于程序 bug 或内存泄露导致的意外崩溃,进程会直接退出,根本来不及执行优雅关闭的逻辑。

  • 根本之策:通过完善的异常捕获try...catch)和日志记录来定位和修复导致崩溃的根源。
  • 兜底方案:在应用启动时(process.on('startup')),从外部存储(如 Redis)中恢复上一次保存的会话或任务状态。
  • 进程管理:利用 PM2 的 --exp-backoff-restart-delay 等策略,防止进程在崩溃后无限次快速重启,给系统恢复和人工介入留出时间。

总结

PM2 解决了 Node.js 进程的可用性问题(崩溃后自动重启),但它无法解决数据持久性问题。

  • 核心原则状态外部化。将任何需要跨重启、跨进程共享的数据,都存储在进程外部的专用系统中(Redis、数据库等)。
  • 实践方法
    • 使用 pm2 reload 代替 pm2 restart 以实现零停机部署。
    • 实现优雅关闭,在进程被主动停止时,完成收尾工作并保存状态。
    • 为意外崩溃设计状态恢复自动重启策略

理解并应用这一模式,是构建一个健壮、高可用的 Node.js 生产环境的基础。