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 生产环境的基础。