本篇文章為大家介紹一下主動觸發Node進程終止的一些方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。
有幾個原因會導致 Node.js 進程終止。其中一些是可以避免的,例如拋出錯誤時,而另一些是無法防止的,例如記憶體不足。全域 process
是一個 Event Emitter 實例,當執行正常退出時,將發出一個 exit
事件。然後程式碼可以透過偵聽這個事件來執行最後的同步清理工作。
相關推薦:《nodejs 教學》
以下是一些可以主動觸發進程終止的方法:
#操作 | |
---|---|
手動流程退出
| process.exit(1)|
未捕獲的異常
| throw new Error()|
未兌現的promise
| Promise.reject()|
忽略的錯誤事件
| EventEmitter#emit('error')|
未處理的訊號
| $ kill ba688ccdd41ae5bf1f639b6cc232fa52
其中有許多是屬於偶然被觸發的,例如未捕獲的錯誤或未處理的 promise,但是其中也有為了直接使進程終止而創建的。
使用 process.exit(code)
來終止進程是最直接的方法。這在當你知道自己的過程已經到了生命週期的盡頭時非常有用。 code
值是可選的,預設值為0,最大可以設為 255。0 表示進程運行成功,而任何非零的數字都表示發生了問題。這些值可以被許多不同的外部工具使用。例如當測試套件執行時,非零值表示測試失敗。
直接呼叫 process.exit()
時,不會寫入任何隱式文字到控制台。如果你編寫了以錯誤表示形式呼叫此方法的程式碼,則你的程式碼應該使用者輸出錯誤來幫助他們解決問題。例如執行以下程式碼:
$ node -e "process.exit(42)" $ echo $?
在這種情況下,單行的 Node.js 程式不會輸出任何訊息,儘管 shell 程式確實會列印退出狀態。遇到這樣的進程退出,用戶將無法理解究竟發生了什麼事。所以要參考下面這段程式配置錯誤時會執行的程式碼:
function checkConfig(config) { if (!config.host) { console.error("Configuration is missing 'host' parameter!"); process.exit(1); } }
在這種情況下,使用者沒會很清楚發生了什麼事。他們運行這個程序,將錯誤輸出到控制台上,並且他們能夠糾正這個問題。
process.exit()
方法非常強大。儘管它在程式碼中有自己的用途,但實際上絕對不應該將其引入可重複使用的庫中。如果在庫中確實發生了錯誤,則應拋出這個錯誤,以便程式可以決定應該如何處理它。
#雖然process.exit()
很有用,但對於執行階段錯誤,你需要使用其他工具。例如當程式正在處理 HTTP 請求時,一般來說錯誤不應該終止進程,而是只傳回錯誤回應。發生錯誤的位置資訊也很有用,這正是應該拋出 Error
物件的地方。
Error
類別的實例包含對導致錯誤的原因有用的元數據,例如堆疊追蹤資訊和訊息字串。從 Error
擴展你自己的錯誤類別是很常見的操作。單獨實例化 Error
不會有太多副作用,如果發生錯誤則必須拋出。
在使用 throw
關鍵字或發生某些邏輯錯誤時,會引發 Error
。發生這種情況時,目前堆疊將會“展開”,這意味著每個函數都會退出,直到一個呼叫函數將呼叫包裝在 try/catch
語句中為止。遇到此語句後,將呼叫 catch
分支。如果錯誤沒有包含在 try/catch
中,則該錯誤被視為未捕獲。
雖然你應該使用帶有Error
的throw
關鍵字,例如throw new Error('foo')
,但從技術上講,你可以拋出任何東西。一旦拋出了什麼東西,它就被認為是一個例外。拋出 Error
實例非常重要,因為捕獲這些錯誤的程式碼很可能會期望得到錯誤屬性。
Node.js 內部庫中常用的另一種模式是提供一個.code
屬性,該屬性是一個字串值,在發行版之間應保持一致。例如錯誤的.code
值是ERR_INVALID_URI
,即使是供人類可讀的.message
屬性可能會更改,但這個code
值也不應被更改。
可悲的是,一種更常用的區分錯誤的模式是檢查 .message
屬性,這個屬性通常是動態的,因為可能回需要修改拼字錯誤。這種方法是很冒險的,也是容易出錯的。 Node.js 生態中沒有完美的解決方案來區分所有函式庫中的錯誤。
當引發未捕獲的錯誤時,控制台中將列印堆疊追蹤訊息,並且進程將回以退出狀態 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
是一個事件發射器,可以透過偵聽 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 拒絕與拋出錯誤非常相似。如果 Promise 中的 reject()
方法被調用,或者在非同步函數中引發了錯誤,則 Promise 可以拒絕。在這方面,以下兩個範例大致相同:
Promise.reject(new Error('oh no')); (async () => { throw new Error('oh no'); })();
這是輸出到控制台的訊息:
(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().
與未擷取的例外不同,從Node.js v14 開始,這些rejection不會使進程崩潰。在未來的 Node.js 版本中,會使目前進程崩潰。當這些未處理的 rejection 發生時,你也可以攔截事件,偵聽 process
物件上的另一個事件:
process.on('unhandledRejection', (reason, promise) => {});
事件发射器是 Node.js 中的常见模式,许多对象实例都从这个基类扩展而来,并在库和程序中使用。它们非常欢迎,值得和 error 与 rejection 放在一起讨论。
当事件发射器发出没有侦听器的 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 }
确保在你使用的事件发射器实例中侦听 error
事件,以便你的程序可以正常处理事件而不会崩溃。
信号是操作系统提供的机制,用于把用数字表示的消息从一个程序发送到另一个程序。这些数字通常用等价的常量字符串来表示。例如,信号 SIGKILL
代表数字信号 9。信号可以有不同的用途,但通常用于终止程序。
不同的操作系统可以定义不同的信号,但是下面列表中的信号一般是通用的:
名称 | 编号 | 可处理 | Node.js 默认 | 信号用途 |
---|---|---|---|---|
SIGHUP |
1 | 是 | 终止 | 父终端已关闭 |
SIGINT |
2 | 是 | 终止 | 终端试图中断,按下 Ctrl + C |
SIGQUIT |
3 | 是 | 终止 | 终端试图退出,按下 Ctrl + D |
SIGKILL |
9 | 否 | 终止 | 进程被强行杀死 |
SIGUSR1 |
10 | 是 | 启动调试器 | 用户定义的信号1 |
SIGUSR2 |
12 | 是 | 终止 | 用户定义的信号2 |
SIGTERM |
12 | 是 | 终止 | 代表优雅的终止 |
SIGSTOP |
19 | 否 | 终止 | 进程被强行停止 |
如果程序可以选择实现信号处理程序,则 Handleable 一列则为是。为否的两个信号无法处理。 Node.js 默认 这一列告诉你在收到信号时,Node.js 程序的默认操作是什么。最后一个信号用途指出了信号对应的作用。
在 Node.js 程序中处理这些信号可以通过侦听 process
对象上的更多事件来完成:
#!/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
信号。切换到另一个终端窗口,并根据输出的进程 ID 值执行以下命令:
$ kill -s SIGHUP <PROCESS_ID>
这演示了一个程序怎样向另一个程序发送信号,并且在第一个终端中运行的 Node.js 程序中输出它所接收到的 SIGHUP
信号。
你可能已经猜到了,Node.js 也能把命令发送到其他程序。可以用下面的命令以把信号从临时的 Node.js 进程发送到你现有的进程:
$ node -e "process.kill(<PROCESS_ID>, 'SIGHUP')"
这还会在你的第一个程序中显示 SIGHUP
消息。现在,如果你想终止第一个进程,要运行下面的命令向其发送不能处理的 SIGKILL
信号:
$ kill -9 <PROCESS_ID>
这时程序应该结束。
这些信号在 Node.js 程序中经常用于处理正常的关闭事件。例如,当 Kubernetes Pod 终止时,它将向程序发送 SIGTERM
信号,之后启动 30 秒计时器。然后程序可以在这 30 秒内正常关闭自己,关闭连接并保存数据。如果该进程在此计时器后仍保持活动状态,则 Kubernetes 将向其发送一个 SIGKILL
。
更多编程相关知识,请访问:编程视频!!
以上是詳解主動終止Node.js進程的幾種方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!