[译]掌握Node.js的核心模块-Process

[译]掌握Node.js的核心模块-Process

在这篇文章中,我们将学习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 的信号时,会触发信号事件,接下来详细阐述其中最重要的两个,STGTERMSIGUSR1

点这里 可以找到所有的支持的信号

STGTERM

STGTERM信号会发送给Node进程以请求终止进程。它与SIGKILL信号不同,可以被监听或者无视

这样可以通过释放进程分配的资源(比如文件描述符或者数据库链接)来以很好的方式关闭进程。这种关闭进程的方式被称为 graceful shutdown

事实上,在演绎一个 graceful shutdown 之前必须经历以下这几个步骤:

  1. 应用收到了停止通知(收到SIGTERM信号)
  2. 应用通知负载均衡器(load balancers)不要再处理新的请求
  3. 应用完成所有正在进行的请求
  4. 接下来,正确释放所有的资源(像数据库连接)
  5. 应用用“成功”的状态码退出(process.exit()

阅读这篇文章了解更多 graceful shutdown in Node.js

SIGUSR1

在 POSIX 的标准中, SIGUSR1SIGUSR2 是可以用在用户定义的条件下,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进程用一个退出的状态码来同步地终止进程,这么会有一些很重要的后果:

  • 它会强制让进程尽快终止
    • 哪怕有些异步操作仍然在进行
    • 因为STDOUTSTDERR的输出的异步的,所以有些日志信息可能会丢失
  • 在大多数情况下,不推荐使用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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容