我現在安裝 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"