[譯]掌握Node.js的核心模塊-Process
- 原文:Mastering the Node.js Core Modules - The Process Module
- 作者:Gergely Nemeth
- 2017年10月25日
在這篇文章中,我們將學(xué)習(xí)Node.js的Process模塊以及模塊里隱藏的寶藏府怯。通讀本文后刻诊,你在編寫生產(chǎn)環(huán)境的應(yīng)用時(shí)會更加自信。你將了解Node.js應(yīng)用中Process的狀態(tài)牺丙、可以正常地關(guān)閉應(yīng)用以及更自信地處理錯(cuò)誤
在新的Mastering the Node.js Core Modules 系列中你可以學(xué)到核心模塊隱藏的或是幾乎無人知曉的特性则涯,以及如何去使用這些特性。過一遍Node.js模塊中基本要素冲簿,你會更加理解Node的運(yùn)行原理以及如何去處理錯(cuò)誤
在這個(gè)章節(jié)中粟判,我們會看一下Node.js的process
模塊。這個(gè)process
對象是EventEmitter
的實(shí)例峦剔。它是一個(gè)全局變量浮入,在當(dāng)前的Node.js進(jìn)程中提供有關(guān)信息。
在 Node.js 的 process 模塊中需要注意的事件(Events)
因?yàn)?code>process模塊是EventEmitter
的實(shí)例羊异,所以你像其他的EventEmitter
的實(shí)例一樣用.on()
方法來訂閱它的事件:
process.on('eventName', () => {
//do something
})
uncaughtException
如果 Javascript 未捕獲的異常事秀,沿著代碼調(diào)用路徑反向傳遞回 Event loop,這個(gè)事件就會被觸發(fā)野舶。
默認(rèn)情況下易迹,如果沒有對uncaughtException
事件添加任何監(jiān)聽器, Node.js 默認(rèn)情況下會將這些異常堆棧打印到stderr平道,然后進(jìn)程退出睹欲。 如果你添加了一個(gè)監(jiān)聽器就會覆蓋上述默認(rèn)行為
process.on('uncaughtException', (err) => {
// here the 1 is a file descriptor for STDERR
//這里的 l 是 STDERR 對應(yīng)的一個(gè)文件描述符
fs.writeSync(1, `Caught exception: ${err}\n`)
})
在過去幾年中,我們見到很多對于這個(gè)事件的錯(cuò)誤的運(yùn)用一屋。當(dāng)使用這個(gè)process
模塊中的uncaughtException
事件時(shí)窘疮,有以下幾點(diǎn)十分重要的建議需要注意
- 如果
uncaughtException
事件發(fā)生了,這表明你的應(yīng)用正處在一個(gè)未定義的狀態(tài)中 - 十分不建議借助
uncaughtException
事件嘗試恢復(fù)應(yīng)用正常運(yùn)行冀墨,這種操作是不安全 - 這個(gè)事件的處理器應(yīng)該只用來進(jìn)行已分配資源的同步清理操作(the handler should only be used for synchronous cleanup of allocated resources)
- 如果處理這個(gè)事件的函數(shù)內(nèi)有異常拋出且未被捕獲的話闸衫,應(yīng)用會立刻退出
- 你應(yīng)該使用外部工具來監(jiān)控你的進(jìn)程并且在必要時(shí)重啟它(比如當(dāng)它崩潰的時(shí)候)
unhandledRejection
如下示例代碼
const fs = require('fs-extra')
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
如果需要復(fù)制的文件不存在的話會發(fā)生什么事?這個(gè)答案取決于你的Node.js的版本诽嘉,通常在4及4以下的版本中蔚出,進(jìn)程不會報(bào)錯(cuò)而是直接退出弟翘,留下坐在電腦前一臉懵逼的你。
而在最近的Node.js版本中骄酗,你會得到如下錯(cuò)誤提示
(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中有個(gè)錯(cuò)誤沒有被捕獲稀余,正確的代碼應(yīng)該這么寫:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
Promise rejections未處理的問題其實(shí)跟uncaughtException
一樣 - 你的 Node.js 進(jìn)程會處于一個(gè)未定義的狀態(tài),更糟糕的是趋翻,這可能會引起文件描述符未關(guān)閉( file descriptor failure )以及內(nèi)存泄漏睛琳。在這種情況下,你最好還是重新啟動Node.js進(jìn)程踏烙。
綜上所述掸掏,你應(yīng)該給unhandledRejection
事件添加監(jiān)聽器并且使用process.exit(1)
來退出進(jìn)程
推薦閱讀 Matteo Collina 的 make-promises-safe package,它可以解決你的困惑
Node.js 信號事件
當(dāng)Node.js進(jìn)程接收到一個(gè) POSIX 的信號時(shí),會觸發(fā)信號事件宙帝,接下來詳細(xì)闡述其中最重要的兩個(gè)丧凤,STGTERM
和SIGUSR1
。
點(diǎn)這里 可以找到所有的支持的信號
STGTERM
STGTERM
信號會發(fā)送給Node進(jìn)程以請求終止進(jìn)程步脓。它與SIGKILL
信號不同愿待,可以被監(jiān)聽或者無視
這樣可以通過釋放進(jìn)程分配的資源(比如文件描述符或者數(shù)據(jù)庫鏈接)來以很好的方式關(guān)閉進(jìn)程。這種關(guān)閉進(jìn)程的方式被稱為 graceful shutdown靴患。
事實(shí)上仍侥,在演繹一個(gè) graceful shutdown 之前必須經(jīng)歷以下這幾個(gè)步驟:
- 應(yīng)用收到了停止通知(收到
SIGTERM
信號) - 應(yīng)用通知負(fù)載均衡器(load balancers)不要再處理新的請求
- 應(yīng)用完成所有正在進(jìn)行的請求
- 接下來,正確釋放所有的資源(像數(shù)據(jù)庫連接)
- 應(yīng)用用“成功”的狀態(tài)碼退出(
process.exit()
)
閱讀這篇文章了解更多 graceful shutdown in Node.js
SIGUSR1
在 POSIX 的標(biāo)準(zhǔn)中鸳君, SIGUSR1
和 SIGUSR2
是可以用在用戶定義的條件下农渊,Node.js 選擇用這個(gè)事件來啟動內(nèi)置的調(diào)試器
你可以用以下命令來給進(jìn)程發(fā)送SIGUSR1
信號
kill -USR1 PID_OF_THE_NODE_JS_PROCESS
一旦你這么做了,所涉及到的Node進(jìn)程會讓你知道調(diào)試器正在運(yùn)行
Starting debugger agent.
Debugger listening on [::]:5858
Process 模塊公開的方法和值
process.cwd()
這個(gè)方法返回Node進(jìn)程的當(dāng)前工作目錄(絕對路徑)
$ node -e 'console.log(`Current directory: ${process.cwd()}`)'
Current directory: /Users/gergelyke/Development/risingstack/risingsite_v2
如果你想改變它或颊,可以調(diào)用process.chdir(path)
這個(gè)方法
process.env
該屬性返回一個(gè)包含用戶環(huán)境的對象砸紊,就像 environ
如果你在根據(jù)十二要素應(yīng)用宣言(the 12-factor application principles)來構(gòu)建應(yīng)用程序,你會十分依賴它囱挑;就像 third principle of a twelve-factor application中說的:all configurations should be stored in the user environment 醉顽。
環(huán)境變量應(yīng)該是首選,因?yàn)檫@樣就可以在不更改代碼的前提下輕松更改不同的部署環(huán)境平挑。不像配置文件(config files)游添,它們基本不可能影響到代碼庫
值得一提的是,你可以更改process.env
中的值通熄,雖然它不會反映到用戶環(huán)境中去
process.exit([code])
這個(gè)方法告訴 Node進(jìn)程用一個(gè)退出的狀態(tài)碼來同步地終止進(jìn)程唆涝,這么會有一些很重要的后果:
- 它會強(qiáng)制讓進(jìn)程盡快終止
- 哪怕有些異步操作仍然在進(jìn)行
- 因?yàn)?code>STDOUT和
STDERR
的輸出的異步的,所以有些日志信息可能會丟失
- 在大多數(shù)情況下唇辨,不推薦使用
process.exit()
- 你可以讓代碼運(yùn)行完畢自動退出作為替代
process.kill(pid, [signal])
你可以用這個(gè)方法來發(fā)送各種 POSIX 信號給各種進(jìn)程廊酣。你不僅僅可以像它名字寫的那樣用來殺死進(jìn)程。這個(gè)命令就像一個(gè)信號發(fā)送器(包括殺死系統(tǒng)調(diào)用)
Node.js 使用的退出代碼
如果一切正常助泽,Node.js 會用退出碼 0
來退出啰扛。如果進(jìn)程因?yàn)槟撤N錯(cuò)誤而退出,你將會獲得以下狀態(tài)碼中的一種:
-
1
: 沒有被uncaughtException
事件的監(jiān)聽器處理的致命異常 -
5
: V8中的致命錯(cuò)誤(比如分配內(nèi)存失斘撕亍) -
9
:無效的參數(shù)隐解,比如指定了未知的選項(xiàng)或者一個(gè)選項(xiàng)引用了一個(gè)未設(shè)定的值
這些只是比較常見的退出碼,想看所有的狀態(tài)碼诫睬,請看 https://nodejs.org/api/process.html#process_exit_codes