我現在安裝 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 服務
-
安裝 nssm
工具
常看我文章的朋友應該都知道,我都是用 Chocolatey 來安裝軟體:
choco install nssm -y
事實上 nssm
是免安裝的綠色軟體,而且只有 nssm.exe
單一執行就可以運行。只要從 官網下載 壓縮檔回來,將 nssm.exe
解壓縮到 C:\Windows
底下就直接可以用了!
注意: 使用 nssm
註冊服務時,它會自動在你的 事件記錄器 中註冊一個 名為 NSSM
的 事件來源 (Event Source)。
-
使用 GUI 介面安裝 Windows 服務
請以系統管理員身份執行 Command Prompt (cmd.exe
),並使用以下命令執行安裝:
nssm install MyService
這個命令事實上會自動開啟 nssm
的 GUI 圖形化介面,非常容易上手好操作:
設定好之後,按下 Install service 就會自動安裝成功!👍
如果要移除 Windows 服務,也可以這樣執行,也一樣會跳出 GUI 介面:
nssm remove MyService
-
使用 CLI 介面安裝 Windows 服務
以下就是一個最簡單的「免互動」安裝 Windows 服務命令:
nssm install MyService "C:\EC.Services\訂單處理服務.exe"
他會自動幫你設定應用程式路徑(Application
)、工作目錄(AppDirectory
)等參數,如果你的程式可以這樣執行,不需要其他應用程式殼層(AppShell),例如 dotnet.exe
或 java.exe
等等。那麼,這確實是最簡單的建立服務方法。
以下是「免互動」重新啟動 Windows 服務命令:
nssm stop MyService
nssm start MyService
nssm restart MyService
以下是「免互動」移除安裝 Windows 服務命令:
nssm remove MyService confirm
-
使用 CLI 介面安裝需要有 應用程式殼層 (AppShell) 才能啟動的 Windows 服務
這邊我說的是像 dotnet.exe
或 java.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 命令提示字元中,一直以來最被詬病的問題,就是「雙引號」的跳脫處理會讓整個命令變的可讀性極差,所以當你需要傳遞參數到服務應用程式時,需要傳入執行的參數包含「雙引號」,那就有地方要特別注意了!
假設你的應用程式參數必須包含空白字元,所以需要使用雙引號框起來:
-
手動執行的寫法
"C:\EC.Services\訂單處理服務.exe" "This is one argument"
-
使用 nssm
的安裝命令
nssm install "訂單處理服務" "C:\EC.Services\訂單處理服務.exe" """This is one argument"""
假設你要執行 Java 應用程式,參數可能很複雜,例如:
-
手動執行的寫法
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%
可以取得當前目錄的路徑!
-
使用 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"
相關連結