[译]掌握Node.js的核心模块-Process
- 原文:Mastering the Node.js Core Modules - The Process Module
- 作者:Gergely Nemeth
- 2017年10月25日
在这篇文章中,我们将学习Node.js的Process模块以及模块里隐藏的宝藏。通读本文后,你在编写生产环境的应用时会更加自信。你将了解Node.js应用中Process的状态、可以正常地关闭应用以及更自信地处理错误
在新的Mastering the Node.js Core Modules 系列中你可以学到核心模块隐藏的或是几乎无人知晓的特性,以及如何去使用这些特性。过一遍Node.js模块中基本要素,你会更加理解Node的运行原理以及如何去处理错误
在这个章节中,我们会看一下Node.js的process
模块。这个process
对象是EventEmitter
的实例。它是一个全局变量,在当前的Node.js进程中提供有关信息。
在 Node.js 的 process 模块中需要注意的事件(Events)
因为process
模块是EventEmitter
的实例,所以你像其他的EventEmitter
的实例一样用.on()
方法来订阅它的事件:
process.on('eventName', () => {
//do something
})
uncaughtException
如果 Javascript 未捕获的异常,沿着代码调用路径反向传递回 Event loop,这个事件就会被触发。
默认情况下,如果没有对uncaughtException
事件添加任何监听器, Node.js 默认情况下会将这些异常堆栈打印到stderr,然后进程退出。 如果你添加了一个监听器就会覆盖上述默认行为
process.on('uncaughtException', (err) => {
// here the 1 is a file descriptor for STDERR
//这里的 l 是 STDERR 对应的一个文件描述符
fs.writeSync(1, `Caught exception: ${err}\n`)
})
在过去几年中,我们见到很多对于这个事件的错误的运用。当使用这个process
模块中的uncaughtException
事件时,有以下几点十分重要的建议需要注意
- 如果
uncaughtException
事件发生了,这表明你的应用正处在一个未定义的状态中 - 十分不建议借助
uncaughtException
事件尝试恢复应用正常运行,这种操作是不安全 - 这个事件的处理器应该只用来进行已分配资源的同步清理操作(the handler should only be used for synchronous cleanup of allocated resources)
- 如果处理这个事件的函数内有异常抛出且未被捕获的话,应用会立刻退出
- 你应该使用外部工具来监控你的进程并且在必要时重启它(比如当它崩溃的时候)
unhandledRejection
如下示例代码
const fs = require('fs-extra')
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
如果需要复制的文件不存在的话会发生什么事?这个答案取决于你的Node.js的版本,通常在4及4以下的版本中,进程不会报错而是直接退出,留下坐在电脑前一脸懵逼的你。
而在最近的Node.js版本中,你会得到如下错误提示
(node:28391) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): undefined
(node:28391) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
这段信息提示我们用来拷贝文件的promise中有个错误没有被捕获,正确的代码应该这么写:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
Promise rejections未处理的问题其实跟uncaughtException
一样 - 你的 Node.js 进程会处于一个未定义的状态,更糟糕的是,这可能会引起文件描述符未关闭( file descriptor failure )以及内存泄漏。在这种情况下,你最好还是重新启动Node.js进程。
综上所述,你应该给unhandledRejection
事件添加监听器并且使用process.exit(1)
来退出进程
推荐阅读 Matteo Collina 的 make-promises-safe package,它可以解决你的困惑
Node.js 信号事件
当Node.js进程接收到一个 POSIX 的信号时,会触发信号事件,接下来详细阐述其中最重要的两个,STGTERM
和SIGUSR1
。
点这里 可以找到所有的支持的信号
STGTERM
STGTERM
信号会发送给Node进程以请求终止进程。它与SIGKILL
信号不同,可以被监听或者无视
这样可以通过释放进程分配的资源(比如文件描述符或者数据库链接)来以很好的方式关闭进程。这种关闭进程的方式被称为 graceful shutdown。
事实上,在演绎一个 graceful shutdown 之前必须经历以下这几个步骤:
- 应用收到了停止通知(收到
SIGTERM
信号) - 应用通知负载均衡器(load balancers)不要再处理新的请求
- 应用完成所有正在进行的请求
- 接下来,正确释放所有的资源(像数据库连接)
- 应用用“成功”的状态码退出(
process.exit()
)
阅读这篇文章了解更多 graceful shutdown in Node.js
SIGUSR1
在 POSIX 的标准中, SIGUSR1
和 SIGUSR2
是可以用在用户定义的条件下,Node.js 选择用这个事件来启动内置的调试器
你可以用以下命令来给进程发送SIGUSR1
信号
kill -USR1 PID_OF_THE_NODE_JS_PROCESS
一旦你这么做了,所涉及到的Node进程会让你知道调试器正在运行
Starting debugger agent.
Debugger listening on [::]:5858
Process 模块公开的方法和值
process.cwd()
这个方法返回Node进程的当前工作目录(绝对路径)
$ node -e 'console.log(`Current directory: ${process.cwd()}`)'
Current directory: /Users/gergelyke/Development/risingstack/risingsite_v2
如果你想改变它,可以调用process.chdir(path)
这个方法
process.env
该属性返回一个包含用户环境的对象,就像 environ
如果你在根据十二要素应用宣言(the 12-factor application principles)来构建应用程序,你会十分依赖它;就像 third principle of a twelve-factor application中说的:all configurations should be stored in the user environment 。
环境变量应该是首选,因为这样就可以在不更改代码的前提下轻松更改不同的部署环境。不像配置文件(config files),它们基本不可能影响到代码库
值得一提的是,你可以更改process.env
中的值,虽然它不会反映到用户环境中去
process.exit([code])
这个方法告诉 Node进程用一个退出的状态码来同步地终止进程,这么会有一些很重要的后果:
- 它会强制让进程尽快终止
- 哪怕有些异步操作仍然在进行
- 因为
STDOUT
和STDERR
的输出的异步的,所以有些日志信息可能会丢失
- 在大多数情况下,不推荐使用
process.exit()
- 你可以让代码运行完毕自动退出作为替代
process.kill(pid, [signal])
你可以用这个方法来发送各种 POSIX 信号给各种进程。你不仅仅可以像它名字写的那样用来杀死进程。这个命令就像一个信号发送器(包括杀死系统调用)
Node.js 使用的退出代码
如果一切正常,Node.js 会用退出码 0
来退出。如果进程因为某种错误而退出,你将会获得以下状态码中的一种:
-
1
: 没有被uncaughtException
事件的监听器处理的致命异常 -
5
: V8中的致命错误(比如分配内存失败) -
9
:无效的参数,比如指定了未知的选项或者一个选项引用了一个未设定的值
这些只是比较常见的退出码,想看所有的状态码,请看 https://nodejs.org/api/process.html#process_exit_codes
网友评论