Node.js 集群模式下 worker 进程异步任务未完成就退出怎么配置?

文章导读
在 Node.js 集群模式下,核心目标是避免 worker 进程在异步任务未完成时强制退出。推荐在 worker 进程内实现优雅退出逻辑,监听系统信号并主动关闭服务句柄,适用于有状态连接或重要异步业务的场景。
📋 目录
  1. 原生 Cluster 与 PM2 信号机制差异
  2. 代码实现方案
  3. 配置进程管理器
  4. 验证方法
  5. 常见坑
  6. 参考来源
A A

在 Node.js 集群模式下,核心目标是避免 worker 进程在异步任务未完成时强制退出。推荐在 worker 进程内实现优雅退出逻辑,监听系统信号并主动关闭服务句柄,适用于有状态连接或重要异步业务的场景。

先说结论:原生 cluster 或 PM2 集群模式下,必须手动处理信号才能实现异步任务完整执行,否则进程会被强制终止。

  • 适合:涉及数据库事务、文件写入或长轮询的接口服务
  • 先准备:确认进程管理工具(原生 cluster 或 PM2)的信号发送机制
  • 验收:重启期间观察日志是否有“连接已关闭”且无报错中断,长耗时请求返回成功

原生 Cluster 与 PM2 信号机制差异

不同管理方式下,worker 接收到的退出信号不同,代码需兼容处理:

  • PM2 / Systemd:重启时默认发送 SIGINTSIGTERM 信号。代码监听这两个信号即可。
  • 原生 Cluster:主进程调用 worker.disconnect() 仅关闭通信通道,不会自动发送 SIGTERM。若希望优雅退出,主进程应先发送自定义消息(如 { cmd: 'shutdown' })或手动发送信号(process.kill(pid, 'SIGTERM')),worker 端需监听对应事件。

以下方案以最常见的 PM2 及监听系统信号为例,原生 Cluster 用户需确保主进程配合发送信号。

代码实现方案

核心思路是监听退出信号,停止接收新请求,等待现有请求及异步任务处理完毕后再关闭进程。

const server = require('http').createServer(app);
const db = require('./db'); // 假设的数据库连接

let isShuttingDown = false;

// 统一处理退出信号
process.on('SIGTERM', handleShutdown);
process.on('SIGINT', handleShutdown);

function handleShutdown() {
  if (isShuttingDown) return;
  isShuttingDown = true;
  console.log('收到退出信号,开始优雅关闭...');

  // 1. 停止接收新连接
  server.close((err) => {
    if (err) {
      console.error('关闭 HTTP 服务出错', err);
    }
  });

  // 2. 关闭数据库连接等资源
  db.close().then(() => {
    console.log('数据库连接已关闭');
    process.exit(0);
  }).catch((err) => {
    console.error('关闭数据库出错', err);
    process.exit(1);
  });

  // 3. 设置强制退出超时,防止死锁
  setTimeout(() => {
    console.error('优雅关闭超时,强制退出');
    process.exit(1);
  }, 10000);
}

配置进程管理器

如果使用 PM2,需在 ecosystem.config.js 中设置 kill_timeout,确保给足代码处理时间,避免 PM2 强制 kill 进程。

module.exports = {
  apps: [{
    name: 'app',
    script: './app.js',
    kill_timeout: 15000, // 需大于代码中的 setTimeout 时间
    wait_ready: true,
    listen_timeout: 10000
  }]
};

验证方法

1. 日志检查

重启服务时,观察日志是否依次打印“收到退出信号”、“数据库连接已关闭”等信息,且进程退出码为 0。

2. 请求测试

在重启期间发送一个耗时较长的请求。例如创建一个休眠 2 秒的接口,同时执行重启:

# 终端 1:发送长请求
curl -v http://localhost:3000/sleep-2s

# 终端 2:触发重启
pm2 restart app

若配置生效,终端 1 应返回 200 状态码;若未生效,请求会失败或挂起。

Node.js 集群模式下 worker 进程异步任务未完成就退出怎么配置?

3. 监控观察

查看错误监控平台,重启时间段内不应出现大量的 502 Bad Gateway 或 ECONNRESET 错误。

常见坑

1. 数据库连接未关闭

只关闭了 HTTP 服务器,没关闭数据库连接池,导致进程无法退出或数据库报连接异常。需在 server.close 回调中或并行关闭 DB 连接。

2. 超时时间设置过短

进程管理器给的等待时间少于业务处理时间,导致代码还没执行完就被强制 kill。需根据业务最长耗时调整 kill_timeout

3. 忽略未捕获异常

异步任务中抛出未捕获异常会导致 worker 直接退出,绕过信号处理。需监听 process.on('uncaughtException') 记录日志后退出。

4. 原生 Cluster 信号缺失

在原生 cluster 模式下,仅监听 SIGTERM 可能无效,因为 worker.disconnect() 不发送该信号。需确认主进程是否发送了信号或自定义消息。

参考来源

  • Node.js Official Documentation, "Cluster", https://nodejs.org/api/cluster.html
  • Node.js Official Documentation, "Process Events", https://nodejs.org/api/process.html#event-signals
  • PM2 Documentation, "Commands", https://pm2.io/docs/runtime/commands/