优雅轻松退出 Node.js 进程

3,179 阅读4分钟

谈退出之前我们还是先谈谈开始一个 Node.js 程序吧。

  • 实用 node 命令来运行一个 js 程序,例如:node server.js
  • 运用封装的命令启动一个服务, 例如:next dev

如果服务的话,node.js 程序不会退出,会在后台启动一个服务进程。由于有了这个服务的进程,就会占有 CPU 资源,在适当的时候,需要退出这个进程,也就是退出这个 Node.js 程序。

Node.js 退出方式

  • 系统级信号被动通知退出
  • 自己要退出

这个和进入职场和出职场一样,要么主动要么被动。

系统级信号

信号名描述
SIGABRT发生致命错误时发出的信号,通常表示需要立即终止进程。
SIGALRM定时器超时时发出的信号。
SIGHUP在控制终端关闭时,操作系统会向进程发送 SIGHUP 信号。
SIGILL发生非法指令时发出的信号,通常表示需要立即终止进程。
SIGINT用户终端(例如按下 Ctrl+C)发出的中断信号,通常表示要求进程退出。
SIGKILL无法被捕获或处理的信号,通常表示需要立即终止进程。
SIGPIPE向一个已经关闭的 socket 写入数据时发出的信号。
SIGQUIT类似于 SIGINT 信号,但是会在进程退出前产生 core dump 文件。
SIGTERM操作系统发出的终止进程信号。
SIGUSR1用户自定义信号 1。
SIGUSR2用户自定义信号 2。
SIGTRAP调试器使用的信号,通常不会出现在普通进程中。
SIGSYS发生系统调用错误时发出的信号。
SIGXCPU进程使用 CPU 时间过长时发出的信号。
SIGXFSZ进程尝试写入一个文件,但文件大小超过文件系统的限制时发出的信号。

经常遇到的两个信号:

  • SIGHUP 关闭终端系统发送信号
  • SIGINT 使用 CTRL + C 发送给 Node.js 的关闭进程信号
process.on('SIGINT', function () {
  console.log('收到 SIGINT 信号,开始优雅退出...');
  // 清理工作...
  process.exit();
});

// 钩子:即将退出之前
process.on('beforeExit', function () {
  console.log('即将退出,开始清理工作...');
  // 清理工作...
});

// 开始退
process.on('exit', function () {
  console.log('退出,开始最后的清理工作...');
  // 清理工作...
});

process.exit 退出不同类型

  • 0:表示进程成功完成。
  • 1:表示进程发生了未知的错误。
  • 2:表示进程调用了不正确的命令或参数。
  • 3:表示进程发生了内部错误或异常。
  • 4:表示进程被强制终止。

例如:要写一个命令行,但是参数长度给的不够,可使用编号 1 来退出错误

if (process.argv.length !== 3) {
  console.error('Usage: node script.js <filename>'); // 退出前要输出错误
  process.exit(1); // 退出并返回退出码 1
}

pm2 和 docker 如何停止 Node.js 进程

  • 使用 pm2 stop 命令发送系统进程命令 SIGINT 来停止当前 Node.js
  • 使用 docker stop 命令,先给容器中的进程发送 SIGTERM, 完成清理工作,其次就是docker 还执行强制措施 SIGKILL 来强制停止应用程序。

当然也可以手动添加监听代码用于监听 SIGINT/SIGTERM 事件,然后执行一些清理等任务。

未捕获的异常与错误

以下是一些常用的 process.on() 方法的事件类型及其说明:

事件类型说明
uncaughtException当一个未被捕获的异常(exception)被抛出时触发。
unhandledRejection当一个 Promise 被 reject,并且没有对应的 catch() 时触发。
warning当 Node.js 发出一个警告(warning)时触发。

当然 Node.js 可以在未捕获的异常发生错误时,退出程序:

process.on('uncaughtException', (error) => {
  console.error('Uncaught exception:', error);
  process.exit(1);
});

// 抛出一个未被处理的异常
throw new Error('Unhandled exception');

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled rejection:', reason);
  process.exit(1);
});

// 抛出一个未被处理的 Promise rejection
Promise.reject(new Error('Unhandled rejection'));

Remix cli 中如何退出应用程序

import { cli } from "./index";

cli.run().then(
  () => {
    process.exit(0);
  },
  (error: unknown) => {
    if (error) console.error(error);
    process.exit(1);
  }
);

cli 中调用 run 方法,run 是个 promise, 在 promise 完成任务会后 exit(0). 但是如果发生了错误,输出错误停止退出。

prisma seed.ts 初始化错误退出

seed()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

小结

优雅退出 Node.js 应用程序,分为主动退出和系统级退出,主动退出常用的分为两种一种是 CTRL + C 和另外一种直接关闭终端工具,本质是用户手动操作,让系统发送命令给 Node.js 进程,停止应用程序。

process.exit 函数根据不同类型进行退出。使用 0 表示成功退出,使用 1 表示有未知错误退出(错误一般用 try-catch 捕获,如果使用 promise 使用 catch 捕获,然后 console 输出)。