The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

介紹好用工具:優異的 nssm 服務管理員 (Non-Sucking Service Manager)

我現在安裝 Windows 服務都使用功能完整nssm 來管理,他除了有 CLI 命令列工具介面外,也同時有支援 GUI 圖形化介面,所以非常容易上手。今天這篇文章我打算來分享幾個常見的用法。

以下命令皆使用 Command Prompt (命令提示字元) (cmd.exe) 來執行。

關於 nssm 服務管理員

Windows 內建的各種註冊 Windows 服務的工具都非常難用(sc.exe),不然就是功能太過陽春(New-Service),而且這些工具沒辦法幫你處理一些程式出錯的狀況,所有例外你都要在自己的程式處理,簡單來說,就是 (Sucks)! 😅

所以 nssm 出現了,這個名字非常諷刺,這四個字就是 Non-Sucking Service Manager 的意思! 😄

nssm 會額外幫你監控服務執行的狀況,如果發生「異常例外」導致程式掛掉,程式自動重啟。即便程式寫成異常發生但「正常結束」,最後 Windows 服務管理員還是會幫你自動重啟。

使用 nssm 安裝並啟動 Windows 服務

  1. 安裝 nssm 工具

    常看我文章的朋友應該都知道,我都是用 Chocolatey 來安裝軟體:

    choco install nssm -y
    

    事實上 nssm 是免安裝的綠色軟體,而且只有 nssm.exe 單一執行就可以運行。只要從 官網下載 壓縮檔回來,將 nssm.exe 解壓縮到 C:\Windows 底下就直接可以用了!

    注意: 使用 nssm 註冊服務時,它會自動在你的 事件記錄器 中註冊一個 名為 NSSM事件來源 (Event Source)。

  2. 使用 GUI 介面安裝 Windows 服務

    請以系統管理員身份執行 Command Prompt (cmd.exe),並使用以下命令執行安裝:

    nssm install MyService
    

    這個命令事實上會自動開啟 nssm 的 GUI 圖形化介面,非常容易上手好操作:

    nssm 的 GUI 圖形化介面

    設定好之後,按下 Install service 就會自動安裝成功!👍

    如果要移除 Windows 服務,也可以這樣執行,也一樣會跳出 GUI 介面:

    nssm remove MyService
    
  3. 使用 CLI 介面安裝 Windows 服務

    以下就是一個最簡單的「免互動」安裝 Windows 服務命令:

    nssm install MyService "C:\EC.Services\訂單處理服務.exe"
    

    他會自動幫你設定應用程式路徑(Application)、工作目錄(AppDirectory)等參數,如果你的程式可以這樣執行,不需要其他應用程式殼層(AppShell),例如 dotnet.exejava.exe 等等。那麼,這確實是最簡單的建立服務方法。

    以下是「免互動」重新啟動 Windows 服務命令:

    nssm stop MyService
    nssm start MyService
    nssm restart MyService
    

    以下是「免互動」移除安裝 Windows 服務命令:

    nssm remove MyService confirm
    
  4. 使用 CLI 介面安裝需要有 應用程式殼層 (AppShell) 才能啟動的 Windows 服務

    這邊我說的是像 dotnet.exejava.exe 這種類型的應用程式,.NET Core 就需要 dotnet.exe MyService.dll 來啟動,而 Java 的 *.jar 就需要執行 java.exe -jar MyService.jar 才能啟動這種。

    這種類型的應用程式,第一次透過 nssm 註冊服務的人,會很容易忘記設定 AppDirectory (工作目錄) 參數,導致應用程式很容易會啟動失敗!

    以下就是一個完整的範例,從安裝服務、設定參數到自動啟動,一口氣設定到好:

    nssm install MyService "C:\Program Files\Zulu\zulu-8\bin\java.exe"
    nssm set MyService AppDirectory "C:\Projects\MyService\target"
    nssm set MyService AppParameters "-jar MyService-0.0.1-SNAPSHOT.jar --server.port=8080"
    nssm set MyService AppStdout "C:\Projects\MyService\target\MyService-0.0.1-SNAPSHOT.log"
    nssm start MyService
    

認識 nssm 的服務執行參數

nssm 提供非常大量的參數可供設定,我也趁著這篇文章全部閱讀過一遍,這才發現 nssm 真的非常強大,果然是我心目中的王者!

以下我列出所有的服務執行參數,這些參數都可以對應到 nssm 的 GUI 圖形化介面上的頁籤,我會額外加上註解說明用途與預設值,詳細說明請見 NSSM Usage 文件!

你可以透過以下命令查詢 nssm 實際註冊到 Windows 服務之後的 nssm 服務執行參數為何。

  • Application

    REM 應用程式路徑
    REM 預設: 應用程式完整路徑 (C:\EC.Services\訂單處理服務.exe)
    nssm get MyService Application
    
    REM 工作目錄
    REM 預設: 應用程式所在目錄路徑 (C:\EC.Services)
    nssm get MyService AppDirectory
    
    REM 應用程式參數
    REM 預設: 無內容
    nssm get MyService AppParameters
    
  • Details

    REM 服務的顯示名稱 (顯示名稱可以跟服務名稱不同)
    REM 預設: 服務名稱 (MyService)
    nssm get MyService DisplayName
    
    REM 服務詳細描述
    REM 預設: 無內容
    nssm get MyService Description
    
    REM 服務啟動方式
    REM 預設: SERVICE_AUTO_START (自動啟動)
    nssm get MyService Start
    
  • Log on

    REM 服務啟動方式
    REM 預設: LocalSystem (本機系統)
    nssm get MyService ObjectName
    
    REM 服務啟動方式
    REM 預設: SERVICE_WIN32_OWN_PROCESS
    nssm get MyService Type
    
  • Dependencies

    REM 相依服務
    REM 預設: 無內容
    nssm get MyService DependOnService
    
  • Process

    REM 執行優先權
    REM 預設: 無內容
    nssm get MyService AppPriority
    
    REM 是否不要啟動一個隱藏的 Console 視窗來執行服務 (有些程式沒有 STDIN 輸入會掛掉)
    REM 預設: 0 (要啟動一個隱藏的 Console 視窗來接受 STDIN 輸入)
    nssm get MyService AppNoConsole
    
    REM 要用多少 CPU 核心執行 (CPU Affinity)
    REM 預設: All (所有 CPU 核心)
    nssm get MyService AppAffinity
    
  • Shutdown

    REM 判斷要用什麼方式傳遞訊號給應用程式
    REM 預設: 0 (代表什麼訊號都傳給應用程式)
    nssm get MyService AppStopMethodSkip
    
    REM 當送出 Control-C 的時候要等待幾豪秒進行優雅結束 (Gracefully Shutdown)
    REM 預設: 1500 豪秒
    nssm get MyService AppStopMethodConsole
    
    REM 當送出 WM_CLOSE 訊號的時候要等待幾豪秒進行優雅結束 (Gracefully Shutdown)
    REM 預設: 1500 豪秒
    nssm get MyService AppStopMethodWindow
    
    REM 當送出 WM_QUIT 訊號的時候要等待幾豪秒進行優雅結束 (Gracefully Shutdown)
    REM 預設: 1500 豪秒
    nssm get MyService AppStopMethodThreads
    
  • Exit actions

    REM 如果程式在幾豪秒之內就死掉就延遲啟動
    REM 預設: 1500 豪秒
    nssm get MyService AppThrottle
    
    REM 應用程式結束時的預設行為
    REM 預設: Restart (重啟服務)
    nssm get MyService AppExit Default
    
    REM 服務重啟的延遲啟動豪秒數
    REM 預設: 0 豪秒
    nssm get MyService AppRestartDelay
    
  • I/O

    REM 應用程式的 STDOUT 訊息要輸出到哪個檔案
    REM 預設: 無內容
    nssm get MyService AppStdout
    
    REM 應用程式的 STDERR 訊息要輸出到哪個檔案
    REM 預設: 無內容
    nssm get MyService AppStderr
    
  • File rotation

    REM 設定是否要對記錄檔進行自動 Rotate 處理
    REM 預設: 1
    nssm get MyService AppRotateFiles
    
    REM STDOUT 訊息紀錄檔的建立方式
    REM 預設: 4 (開啟現有的紀錄檔進行寫入)
    nssm get MyService AppStdoutCreationDisposition
    
    REM STDERR 訊息紀錄檔的建立方式
    REM 預設: 4 (開啟現有的紀錄檔進行寫入)
    nssm get MyService AppStderrCreationDisposition
    
    REM 服務在執行的時候也可以 Rotate 記錄檔
    REM 預設: 0 (預設要停用服務才會 Rotate 記錄檔)
    nssm get MyService AppRotateOnline
    
    REM 當記錄檔已經超過幾豪秒就自動 Rotate
    REM 預設: 86400
    nssm get MyService AppRotateSeconds
    
    REM 當記錄檔已經超過多少 Bytes 就自動 Rotate
    REM 預設: 1048576
    nssm get MyService AppRotateBytes
    
  • Environment

    REM 服務啟動前先設定好環境變數
    REM 預設: 無內容
    nssm get MyService AppEnvironmentExtra
    

注意「雙引號」傳遞應用程式參數可能造成的問題

在 Command Prompt 命令提示字元中,一直以來最被詬病的問題,就是「雙引號」的跳脫處理會讓整個命令變的可讀性極差,所以當你需要傳遞參數到服務應用程式時,需要傳入執行的參數包含「雙引號」,那就有地方要特別注意了!

假設你的應用程式參數必須包含空白字元,所以需要使用雙引號框起來:

  1. 手動執行的寫法

    "C:\EC.Services\訂單處理服務.exe" "This is one argument"
    
  2. 使用 nssm 的安裝命令

    nssm install "訂單處理服務" "C:\EC.Services\訂單處理服務.exe" """This is one argument"""
    

假設你要執行 Java 應用程式,參數可能很複雜,例如:

  1. 手動執行的寫法

    REM 設定 JavaExe 變數不需要加上雙引號
    JavaExe=C:\Program Files\Zulu\zulu-8\bin\java.exe
    
    "%JavaExe%" -Dsolr.solr.home="%CD%\solr" -Djetty.home="%CD%" -Djetty.logs="%CD%\logs" -cp "%CD%\lib\*.jar";"%CD%\start.jar" -jar "%CD%\start.jar"
    

    使用 %CD% 可以取得當前目錄的路徑!

  2. 使用 nssm 的安裝命令

    REM 設定 JavaExe 變數不需要加上雙引號
    JavaExe=C:\Program Files\Zulu\zulu-8\bin\java.exe
    
    nssm install solr "%JavaExe%" -Dsolr.solr.home="\"%CD%\solr"\" -Djetty.home="\"%CD%"\" -Djetty.logs="\"%CD%\logs"\" -cp "\"%CD%\lib\*.jar"\";"\"%CD%\start.jar"\" -jar "\"%CD%\start.jar"\"
    

    你看看,這段批次檔多醜啊! 😅

注意設定環境變數的寫法

在服務啟動之前設定好環境變數,這應該是我最愛 nssm 的功能之一了!

架設我希望在執行 MyService 服務之前,先給定三個環境變數,分別是 A=1, B=2, C=3 等等,那麼你應該在建立好服務且尚未啟動服務之前,先這樣設定好環境變數:

nssm set MyService AppEnvironmentExtra "A=1" "B=2" "C=3"

相關連結

留言評論