详细谈谈NodeJS进程是如何退出的

 更新时间:2021年07月14日 11:55:16   作者:众里千寻  
这篇文章主要给大家介绍了关于NodeJS进程是如何退出的相关资料,主要介绍了导致进程退出的三个因素:主动退出;未捕获的异常、未处理的 promise rejection;未处理的 Event Emitter error 事件 系统信号,需要的朋友可以参考下

前言

有几种因素可以导致 NodeJS 进程退出。在这些因素中,有些是可预防的,比如代码抛出了一个异常;有些是不可预防的,比如内存耗尽。process 这个全局变量是一个 Event Emitter 实例,如果进程优雅退出,process 会派发一个 exit 事件。应用代码可以监听这个事件,来做最后的清理工作。

下面的表格列举了可以导致进程退出的因素。

操作 举例
手动退出 process.exit(1)
未捕获的异常 throw new Error()
未处理的 promise rejection Promise.reject()
未处理的 error 事件 EventEmitter#emit('error')
未处理的信号 kill <PROCESS_ID>

主动退出

process.exit(code) 是最直接的结束进程的方法。code 参数是可选的,可以为 0 ~ 255 之间任何数字,默认为 0。0 表示进程执行成功,非 0 数字表示进程执行失败。

当 process.exit() 被使用时,控制台不会有任何输出,如果我们想在进程推出的时候像控制台输出一些错误说明信息,则需要在调用之前显示的输出错误信息。

node -e "process.exit(42)"
echo $?

上面的代码直接退出了 NodeJS 进程,命令行没有任何输出信息。用户在遭遇进程退出的时候,无法获取有效的错误信息。

function checkConfig(config) {
  if (!config.host) {
    console.error("Configuration is missing 'host' parameter!");
    process.exit(1);
  }
}

在上面的代码中,我们在进程退出之前输出的明确的错误信息。

process.exit() 的功能非常强大,但是我们不应该在工具库中使用。如果在工具库中遇到的错误,我们应该以异常的形式抛出,从而让应用代码决定如何处理这个错误。

Exceptions, Rejections 和 Emitted Errors

process.exit() 在应用启动配置检查等场景中非常有用,但是在处理运行时异常时,它并不适用,我们需要其他的工具。

比如当应用在处理一个 HTTP 请求时,一个错误不应该导致进程终止,相反,我们应该返回一个含有错误信息的响应。

Error 类可以包含描述错误发生的详细信息的数据,比如调用堆栈和错误文本。通常我们会定义特定场景的 XXXError,这些 XXXError 都继承制 Error 类。

当我们使用 throw 关键字,或者代码逻辑出错时,一个错误就会被抛出。此时,系统调用栈会释放,每个函数会退出,直到遇到一个 包裹了当前调用的 try/catch 语句。如果没有 try/catch 语句,则这个错误会被认为是未捕获的异常。

通常,在 NodeJS 应用中,我们会给 Error 类定义一个 code 属性,作为用来描述具体错误的错误码,这么做的优点是可以使错误码保持唯一,同时还能使得错误码是可读的。同时,我们也可以配合 message 属性来描述具体的错误信息。

当一个未捕获的异常抛出时,控制台会打印调用堆栈,同时进程退出,退出状态码为 1.

/tmp/foo.js:1
throw new TypeError('invalid foo');
^
Error: invalid foo
    at Object.<anonymous> (/tmp/foo.js:2:11)
    ... TRUNCATED ...
    at internal/main/run_main_module.js:17:47

这段控制台输出信息说明,错误发生在 foo.js 中的第 2 行第 11 列。

全局变量 process 是个 Event Emitter 实例,可以通过监听 uncaughtException 事件来处理这些未捕获异常。下面的代码展示了如何使用:

const logger = require("./lib/logger.js");
process.on("uncaughtException", (error) => {
  logger.send("An uncaught exception has occured", error, () => {
    console.error(error);
    process.exit(1);
  });
});

Promise Rejection 与抛出异常类似。我们可以通过调用 reject() 函数或者在 async 函数中抛出异常来是的 promise 到达 rejected 状态。下面的两段代码功能是相似的。

Promise.reject(new Error("oh no"));

(async () => {
  throw new Error("oh no");
})();

目前,在 NodeJS 14 中,Promise Rejection 不会导致进程退出,在后续的版本中,Promise Rejection 可能会导致进程退出。

下面是一段未捕获的 Promise Rejection 的控制台输出样例。

(node:52298) UnhandledPromiseRejectionWarning: Error: oh no
    at Object.<anonymous> (/tmp/reject.js:1:16)
    ... TRUNCATED ...
    at internal/main/run_main_module.js:17:47
(node:52298) UnhandledPromiseRejectionWarning: Unhandled promise
  rejection. This error originated either by throwing inside of an
  async function without a catch block, or by rejecting a promise
  which was not handled with .catch().

我们可以通过监听 unhandledRejection 事件来处理未捕获的 Rejection. 样例代码如下:

process.on("unhandledRejection", (reason, promise) => {});

Event Emitter 是 NodeJS 中的基础模块,应用广泛。当 Event Emitter 的 error 事件未被处理时,Event Emitter 就会抛出一个错误,同时会导致进程退出。下面是一个 Event Emitter error 的控制台输出。

events.js:306
    throw err; // Unhandled 'error' event
    ^
Error [ERR_UNHANDLED_ERROR]: Unhandled error. (undefined)
    at EventEmitter.emit (events.js:304:17)
    at Object.<anonymous> (/tmp/foo.js:1:40)
    ... TRUNCATED ...
    at internal/main/run_main_module.js:17:47 {
  code: 'ERR_UNHANDLED_ERROR',
  context: undefined
}

因此,我们在使用 Event Emitter 的时候,要确保监听了 error 事件,这样在发生错误的时候,可以使得应用能够处理这些错误,避免奔溃。

信号

信号是操作信息提供了进程间通信机制。信号通常是一个数字,同时也可以使用一个字符串来标识。比如 SIGKILL 标识数字 9。不同的操作系统对信号的定义不同。下面表格里罗列的是基本通用的信号定义。

名称 数字 是否可处理 NodeJS 默认行为 信号的含义
SIGHUP 1 Yes 退出 父命令行被关闭
SIGINT 2 Yes 退出 命令行尝试中断,即 Ctrl + C
SIGQUIT 3 Yes 退出 命令行尝试退出,即 Ctrl + Z
SIGKILL 9 No 退出 强制进程退出
SIGUSR1 10 Yes 启动调试器 用户自定义信号
SIGUSR2 12 Yes 退出 用户自定义信号
SIGTERM 15 Yes 退出 进程优雅的退出
SIGSTOP 19 No 退出 进程被强制停止

这表格里,是否可处理表示这个信号是否可被进程接收并被处理。NodeJS 默认行为表示进程在接收到这个信号以后默认执行的动作。

我们可以通过如下方式来监听这些信号。

#!/usr/bin/env node
console.log(`Process ID: ${process.pid}`);
process.on("SIGHUP", () => console.log("Received: SIGHUP"));
process.on("SIGINT", () => console.log("Received: SIGINT"));
setTimeout(() => {}, 5 * 60 * 1000); // keep process alive

在一个命令行窗口中运行这段代码,然后按下 Ctrl + C,此时进程不会退出,而是会在控制台打印一行接收到了 SIGINT 信号的日志信息。新起一个命令行窗口,执行如下命令,PROCESS_ID 为上面程序输出的进程 ID。

kill -s SIGHUP <PROCESS_ID>

通过新起的命令行,我们向原来的那个程序进程发送了一个 SIGHUP 信号,原来的命令行窗口中会打印一行接收到了 SIGHUP 信号的日志信息。

在 NodeJS 代码中,进程也可以给其他进程发送信号。比如:

node -e "process.kill(<PROCESS_ID>, 'SIGHUP')"

这段代码同样会在第一个命令行窗口中输出一行接收到了 SIGHUP 信号的日志。

如果我们要让第一个命令行窗口的进程退出,则可以通过下面的命令来实现。

kill -9 <PROCESS_ID>

在 NodeJS 中,信号通常被用作控制进程优雅的退出。比如,在 Kubernetes 中,当一个 pod 要退出时,k8s 会像 pod 内的进程发送一个 SIGTERM 的信号,同时启动一个 30 秒的定时器。应用程序有 30 秒的时间来关闭连接、保存数据等。如果 30 秒之后进程依然存活,k8s 会再发送一个 SIGKILL 来强制关闭进程。

小结

本文讲述了可以导致进程退出的几种因素,分别是:

  • 主动退出
  • 未捕获的异常、未处理的 promise rejection、未处理的 Event Emitter error 事件
  • 系统信号

到此这篇关于NodeJS进程是如何退出的文章就介绍到这了,更多相关NodeJS进程退出内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • Webpack 实现 Node.js 代码热替换

    Webpack 实现 Node.js 代码热替换

    Webpack有一个很实用的功能叫做热替换(Hot-replace),尤其是结合React Hot Loader插件,开发过程中都不需要刷新浏览器,任何前端代码的更改都会实时的在浏览器中表现出来。
    2015-10-10
  • express框架实现基于Websocket建立的简易聊天室

    express框架实现基于Websocket建立的简易聊天室

    本篇文章主要介绍了express框架实现基于Websocket建立的简易聊天室,具有一定的参考价值,有兴趣的可以了解一下
    2017-08-08
  • 在Node.js中执行解压缩文件操作方法

    在Node.js中执行解压缩文件操作方法

    本文主要介绍了如何在Node.js中进行解压缩文件操作,本文也提供了压缩文件的方法,压缩文件的文件路径需要对应自己要压缩的文件夹路径,通过本文的介绍,可以帮助读者更好地理解和使用Node.js进行解压缩文件操作
    2024-10-10
  • nvm管理node版本的详细图文教程

    nvm管理node版本的详细图文教程

    nvm全英文也叫node.js version management,是一个nodejs的版本管理工具,下面这篇文章主要给大家介绍了关于nvm管理node版本的详细图文教程,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • node.js中的console.timeEnd方法使用说明

    node.js中的console.timeEnd方法使用说明

    这篇文章主要介绍了node.js中的console.timeEnd方法使用说明,本文介绍了console.timeEnd的方法说明、语法、使用实例和实现源码,需要的朋友可以参考下
    2014-12-12
  • 切换node版本的实现方式

    切换node版本的实现方式

    由于有些比较老的系统,使用的node版本较低,本机装的node版本又比较高,不想降node版本,那么就需要考虑能不能在系统里管理多个node版本,本文主要介绍了切换node版本,感兴趣的可以了解一下
    2024-04-04
  • 整理 node-sass 安装失败的原因及解决办法(小结)

    整理 node-sass 安装失败的原因及解决办法(小结)

    这篇文章主要介绍了整理 node-sass 安装失败的原因及解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • windows 下安装nodejs 环境变量设置

    windows 下安装nodejs 环境变量设置

    windows 下安装nodejs 了,也安装了npm, 但是有时候切不能直接用request(‘ws’)这一类的东西.我觉得是确实环境变量或其他设置有问题,能否给个完整的设置方案:
    2017-02-02
  • nodejs一个简单的文件服务器的创建方法

    nodejs一个简单的文件服务器的创建方法

    这篇文章主要介绍了nodejs一个简单的文件服务器的创建方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-09-09
  • nodeJs项目在阿里云的简单部署

    nodeJs项目在阿里云的简单部署

    这篇文章主要为大家详细介绍了nodeJs项目在阿里云的简单部署,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11

最新评论