首頁  >  文章  >  web前端  >  詳解主動終止Node.js進程的幾種方法

詳解主動終止Node.js進程的幾種方法

青灯夜游
青灯夜游轉載
2021-04-22 09:47:575900瀏覽

本篇文章為大家介紹一下主動觸發Node進程終止的一些方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳解主動終止Node.js進程的幾種方法

有幾個原因會導致 Node.js 進程終止。其中一些是可以避免的,例如拋出錯誤時,而另一些是無法防止的,例如記憶體不足。全域 process 是一個 Event Emitter 實例,當執行正常退出時,將發出一個 exit 事件。然後程式碼可以透過偵聽這個事件來執行最後的同步清理工作。

相關推薦:《nodejs 教學

以下是一些可以主動觸發進程終止的方法:

範例process.exit(1)throw new Error()Promise.reject()EventEmitter#emit('error')$ kill ba688ccdd41ae5bf1f639b6cc232fa52
#操作
手動流程退出
未捕獲的異常
未兌現的promise
忽略的錯誤事件
未處理的訊號
#####

其中有許多是屬於偶然被觸發的,例如未捕獲的錯誤或未處理的 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() 方法非常強大。儘管它在程式碼中有自己的用途,但實際上絕對不應該將其引入可重複使用的庫中。如果在庫中確實發生了錯誤,則應拋出這個錯誤,以便程式可以決定應該如何處理它。

exceprion、rejection 和發出的Error

#雖然process.exit() 很有用,但對於執行階段錯誤,​​你需要使用其他工具。例如當程式正在處理 HTTP 請求時,一般來說錯誤不應該終止進程,而是只傳回錯誤回應。發生錯誤的位置資訊也很有用,這正是應該拋出 Error 物件的地方。

Error 類別的實例包含對導致錯誤的原因有用的元數據,例如堆疊追蹤資訊和訊息字串。從 Error 擴展你自己的錯誤類別是很常見的操作。單獨實例化 Error 不會有太多副作用,如果發生錯誤則必須拋出。

在使用 throw 關鍵字或發生某些邏輯錯誤時,會引發 Error。發生這種情況時,目前堆疊將會“展開”,這意味著每個函數都會退出,直到一個呼叫函數將呼叫包裝在 try/catch 語句中為止。遇到此語句後,將呼叫 catch 分支。如果錯誤沒有包含在 try/catch 中,則該錯誤被視為未捕獲。

雖然你應該使用帶有Errorthrow 關鍵字,例如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(&#39;./lib/logger.js&#39;);
process.on(&#39;uncaughtException&#39;, (error) => {
  logger.send("An uncaught exception has occured", error, () => {
    console.error(error);
    process.exit(1);
  });
});

Promise 拒絕與拋出錯誤非常相似。如果 Promise 中的 reject() 方法被調用,或者在非同步函數中引發了錯誤,則 Promise 可以拒絕。在這方面,以下兩個範例大致相同:

Promise.reject(new Error(&#39;oh no&#39;));

(async () => {
  throw new Error(&#39;oh no&#39;);
})();

這是輸出到控制台的訊息:

(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(&#39;unhandledRejection&#39;, (reason, promise) => {});

事件发射器是 Node.js 中的常见模式,许多对象实例都从这个基类扩展而来,并在库和程序中使用。它们非常欢迎,值得和 error 与 rejection 放在一起讨论。

当事件发射器发出没有侦听器的 error 事件时,将会抛出所发出的参数。然后将抛出出一个错误并导致进程退出:

events.js:306
    throw err; // Unhandled &#39;error&#39; 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: &#39;ERR_UNHANDLED_ERROR&#39;,
  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(&#39;SIGHUP&#39;, () => console.log(&#39;Received: SIGHUP&#39;));
process.on(&#39;SIGINT&#39;, () => console.log(&#39;Received: SIGINT&#39;));
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>, &#39;SIGHUP&#39;)"

这还会在你的第一个程序中显示 SIGHUP 消息。现在,如果你想终止第一个进程,要运行下面的命令向其发送不能处理的 SIGKILL 信号:

$ kill -9 <PROCESS_ID>

这时程序应该结束。

这些信号在 Node.js 程序中经常用于处理正常的关闭事件。例如,当 Kubernetes Pod 终止时,它将向程序发送 SIGTERM 信号,之后启动 30 秒计时器。然后程序可以在这 30 秒内正常关闭自己,关闭连接并保存数据。如果该进程在此计时器后仍保持活动状态,则 Kubernetes 将向其发送一个 SIGKILL

更多编程相关知识,请访问:编程视频!!

以上是詳解主動終止Node.js進程的幾種方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除