node中建立服務程式

欲休發表於2017-05-10

背景

在node工程部署中,常常涉及到三方:本地客戶端、跳板機和伺服器(叢集)。在通過git觸發gitlab hook指令碼後,需要在跳板機中執行相應的ssh命令執行shell檔案啟動node伺服器,這需要使用一個常用的命令setsid,這樣當ssh命令執行完畢shell退出後,node伺服器仍正常執行,此時node服務程式就是一個最典型的daemon程式(後臺服務程式)。

那麼,在node專案中,如何建立一個daemon程式呢?最簡單的方式,其實就是採用類似上文中介紹的方式:

這樣可以通過執行shell的方式實現daemon程式。不過本文的重點並不是介紹這種“命令列”的方式實現daemon程式,而且本文會詳細講述daemon程式的建立原理,且看下文。

目標

在當前業務中,之所以需要建立daemon程式就是為了保證中斷建立該程式的父程式(ctrl+c)或者父程式執行完畢後並不影響daemon程式的執行。下文介紹兩種實現方式,實現原理細節上有些出入。

下文中的所有討論都是在linux環境下進行。

實現一

在linux系統中,父程式建立出子程式,此時父程式若退出,此時子程式則變為孤兒程式,其ppid變為1,即成為init程式的子程式。在node環境下,如果不針對子程式的stdio做一些特殊處理父程式其實不會真正退出,而是直到子程式執行完畢後再退出。之所以出現這種情況是由於node建立子程式時預設會通過pipe方式將子程式的輸出導流到父程式的stream中(childProcess.stdout、childProcess.stderr),提供在父程式中輸出子程式訊息的能力。

因此,解決此種問題可給子程式的stdio重新賦值:

通過在parent.js中設定子程式的stdio為當前終端(其實繼承了父程式的stdio),這樣父程式在5s後退出,此時子程式的ppid變為1,10s後子程式退出。

上述實現只滿足“父程式正常退出,子程式成為守護程式”的情況,一旦通過“ctrl+c”的方式終端父程式,子程式仍會退出,這還是與node底層實現有關。預設“ctrl+c”觸發SIGINT訊號,父程式接受訊號後傳送給子程式,如果子程式存在SIGINT偵聽函式,則會執行該函式,否則執行exit系統呼叫子程式退出。因此,如果要讓子程式在接收到SIGINT訊號不退出,只需要不作處理即可:

以上實現,可以滿足我們最初指定的目標:“父程式退出或者中斷,子程式仍正常執行”

實現二

node官方提供了建立daemon程式的相關API,如果不仔細閱讀文件還真不容易發現該特性。在child_process模組中有個spawn函式,通過spawn可以執行shell命令及其相關選項,同時spawn提供了建立子程式的一些選項,其中“detached”選項則與我們的需求密切相關。

detached選項可以讓node原生幫我們建立一個daemon程式,設定datached為true可以建立一個新的session和程式組,子程式的pid為新建立程式組的組pid,這與setsid起到相同的作用。此時的子程式已經和其父程式屬於兩個session,因此父程式的退出和中斷訊號不會傳遞給子程式,子程式不會接受到父程式的中斷訊號自然也不會退出。當父程式結束之後,子程式變為孤兒程式從而被init程式接收,ppid設定為1。

此時,c.js檔案並未設定SIGINT事件偵聽函式,在父程式中斷後仍會正常執行,正是由於其和父程式分屬於兩個session。

在parent.js檔案中設定了sp.unref()函式,目的是“避免父程式等待子程式退出”。那麼為何會出現上述情況呢?這與node的事件迴圈有關,讓父程式的事件迴圈排除對ChildProcess子程式物件的引用,可以使父程式單獨退出。

總結

為什麼上文介紹的兩個方法都可以實現daemon程式呢?這還得回到系統層面進行分析。在linux系統建立一個daemon程式需要幾個步驟:

  1. 父程式建立子程式,父程式退出,讓子程式成為孤兒程式,ppid=1
  2. 通過setsid命令或函式在子程式中建立新的會話和程式組
  3. 設定當前目錄
  4. 設定檔案許可權,並關閉父程式繼承開啟的fd

所謂會話和程式組,則是在linux多工多使用者下的概念。不同會話的程式無法通過通訊,因此父子程式相隔離。而執行setsid命令則讓子程式有了新的特性:

  • 子程式脫離父程式所在的session控制,兩者獨立存在互不影響
  • 子程式脫離父程式所在的程式組
  • 子程式脫離原先的命令列終端,終端退出不影響子程式

下面再回顧方法一方法二的區別,發現方法一其實並不是真正的daemon程式,只是通過偵聽相關中斷訊號並設定nop函式(不執行預設的中斷行為)保證子程式繼續執行而已;而方法二則是標準的deamon程式建立方式,優先使用!

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

node中建立服務程式

相關文章