最稳妥的方案是在代码中实现优雅退出逻辑,配合 PM2 的 kill_timeout 配置,并在更新时使用 reload 命令而非 restart。
先说结论:单纯依赖 PM2 配置无法完全保证异步任务不丢失,必须结合代码层的优雅关闭处理。
- 适合:有定时任务、消息队列消费或长连接服务的 Node.js 应用
- 先准备:确认代码中监听了 SIGINT 和 SIGTERM 信号,并持有 server 实例
- 验收:通过手动发送停止信号观察日志中是否有"Server closed"字样
原理简述
PM2 在重启应用时,默认行为是先发送 SIGTERM 信号,等待 kill_timeout 设定的时间。如果超时后进程仍未退出,PM2 会发送 SIGKILL 强制杀死进程。这意味着正在执行的异步操作(如数据库写入、文件上传)若未在超时窗口内完成,会被强制截断。
实战代码:Express 服务优雅关闭完整示例
以下代码展示了如何监听信号、停止接收新请求、等待现有请求完成后再关闭服务器。
const express = require('express');
const app = express();
const server = app.listen(3000);let isShuttingDown = false;
let pendingRequests = 0;// 中间件:统计正在处理的请求
app.use((req, res, next) => {if (isShuttingDown) {return res.sendStatus(503); // 拒绝新请求}pendingRequests++;res.on('finish', () => {pendingRequests--;checkExit(); // 请求完成后检查是否可退出});next();
});function checkExit() {if (isShuttingDown && pendingRequests === 0) {server.close(() => {console.log('Server closed successfully');process.exit(0);});}
}// 监听终止信号
process.on('SIGTERM', () => {console.log('SIGTERM received, starting graceful shutdown');isShuttingDown = true;checkExit();
});process.on('SIGINT', () => {console.log('SIGINT received, starting graceful shutdown');isShuttingDown = true;checkExit();
});PM2 配置文件参考
在 ecosystem.config.js 中调整超时时间和重启策略。kill_timeout 需大于业务中最长异步任务的耗时。
module.exports = {apps: [{name: 'my-app',script: './app.js',kill_timeout: 10000, // 建议 10s 以上,根据业务耗时调整,单位毫秒max_memory_restart: '1G',instances: 4, // 集群模式exec_mode: 'cluster'}]
}部署与验证命令
# 更新应用时零宕机重载(推荐)
pm2 reload all# 查看应用状态及重启次数
pm2 list# 监控日志观察退出过程(修正语法)
pm2 logs `--lines` 100验证是否生效
1. 触发重启:运行 pm2 reload all 或手动发送信号 kill -SIGTERM <pid>。
2. 观察日志:在 pm2 logs 中搜索以下关键词:
- SIGTERM received, starting graceful shutdown(表示信号已接收)
- Server closed successfully(表示连接已安全关闭)
3. 检查数据:确认数据库或文件系统中没有产生残缺数据,且 PM2 状态栏中重启次数(restart count)未异常增加。
常见坑与排查
1. 直接调用 process.exit():代码中若直接调用 process.exit(0) 会跳过 server.close() 回调,导致连接强制断开。
2. kill_timeout 设置不当:设置过短(如 3000ms)可能导致长任务被强制杀死;设置过长会导致部署变慢。建议根据业务最长耗时预留 2-3 倍缓冲。
3. 集群模式协调:在 cluster 模式下,PM2 会向每个实例发送信号。确保每个实例都能独立处理关闭逻辑,不要依赖进程间共享变量判断退出状态。
4. 定时器未清除:除了 HTTP 请求,若有 setInterval 等定时器,需在关闭逻辑中手动 clearInterval,否则进程无法退出。
参考来源
- PM2 Official Documentation - Graceful Shutdown, https://pm2.io/doc/en/runtime/guide/graceful-shutdown/
- PM2 Official Documentation - Memory Management, https://pm2.io/doc/en/runtime/guide/memory-management/
原文链接:https://www.zjcp.cc/ask/10951.html
