Worker Service初探

WorkService

由於近期改為.Net6進行開發,故對應的背景服務也必須同步至.Net6。
背景服務是很多系統都會開發到的一角,只是在Net Framework時為Window Service,可是從dotnet Core開始、到現在的.Net6,已經不再是Window Service,而是改為Worker Service。

Worker Service說明圖1

 

專案建立後,預設架構與程式如下:
Worker Service說明圖2 Worker Service說明圖3

 

首先加入Nuget參考「Microsoft.Extensions.Hosting.WindowsServices」,加入後即可在Program.cs中加入.UseWindowsService()。
網路上範本說明均會在此指定ServiceName,但由於後續安裝是透過sc指令進行安裝服務、而sc指令需指定Service Name,導致程式中是否有指定ServiceName已經不重要;經實測證明:程式是否有指定ServiceName、或與sc指令中的服務名稱不同,最後都是以sc指令為主。

※ 使用.net6的Worker Service時,為Visual Studio 2022 版本17.3.3、.Net 6.0.9,以及Nuget版本Microsoft.Extensions.Hosting.WindowsServices 6.0.1,若未來有版本更新導致差異,還請自行至微軟官網查詢。

Worker Service說明圖4

Worker.cs則是服務執行的內容,預設只有ExecuteAsync覆寫實作,而StartAsync服務啟動、StopAsync服務停止則類似於過去WindowsService的OnStart、OnStop方法,只是在.Net6中有需要才需進行覆寫;另外值得注意的是,若有覆寫StartAsync、StopAsync時,則必須呼叫「await base.StartAsync (stoppingToken);」、「await base.StopAsync(stoppingToken);」。

初次使用,以單純的寫檔來觀察每個方法的執行:
Worker Service說明圖5

Worker Service說明圖6

可透過寫檔的內容可知:服務啟動後,只會呼叫一次ExecuteAsync,所以一般服務所須執行的內容,例如Timer、Thread等都需在此初始化並呼叫完畢、並啟動迴圈執行;最後在結束時(例如StopAsync)進行釋放。

※ 據微軟官網表示:Worker Service並不會啟用GC,所以若有需要則須自行在專案檔中追加設定ServerGarbageCollection為true,此設定項目無法透過專案屬性視窗進行設定。

Worker Service說明圖7

接著說明一下發佈與安裝:發佈時個人習慣採用發行至資料夾,此處須注意的是要追加勾選「產生單一檔案」,這樣一來發佈出的檔案數才會少一點(個人實測:若沒有勾選該選項,發佈出來的檔案個數有258個)。

Worker Service說明圖8

Worker Service說明圖9

安裝方法如前文所提,以sc指令進行(此處服務名稱為Ite2Amy):
(1) 安裝服務:sc.exe create Ite2Amy binpath=”C:\程式路徑\WorkerService1.exe”
(2) 服務啟動:sc start Ite2Amy
(3) 服務停止:sc stop Ite2Amy
(4) 卸載服務:sc.exe delete Ite2Amy

Worker Service說明圖10

最後來說一下關於config的事情:在Program.cs中追加ConfigureAppConfiguration載入config、而後便可以在Worker.cs中取得configuration。

一開始我是直接嘗試網路上的範例,然後就卡關了,然而在debug模式下執行卻是沒有問題的、只有發佈成服務執行時才會出問題。

Worker Service說明圖11

Worker Service說明圖12

這問題的錯誤訊息可以在事件檢視器中查到:「The configuration file ‘appsettings.json’ was not found and is not optional. The expected physical path was ‘C:\WINDOWS\system32\appsettings.json’.」。

Worker Service說明圖13

故此處用寫檔的方式觀察一下服務啟動時的相關路徑:
Worker Service說明圖14

Worker Service說明圖15

認真說執行結果還是挺令人驚訝的,因為透過Assembly取得程式路徑在.net Framework中是很常使用到的方式,但很明顯在此處完全行不通、取得路徑都是空白而無法使用,不過還是可以使用AppDomain的BaseDirectory;除此之外,不要SetBasePath也可以解決路徑問題。就實測結果來看,吃檔的預設目錄就已經是程式路徑,所以不要多設定就好。

Worker Service說明圖16

除此之外,AddJsonFile還有另外兩個選項optional、reloadOnChange,在未設定時均預設為false。

其中若optional設定為true,即使該檔案偵測不到也不會出現錯誤、讓服務可以正常啟動,而服務執行過程中取configuration的值時將以空值提供;至於reloadOnChange則是當檔案有變更時重新載入,但需考慮服務執行一段時間後變更設定是否會出現衍伸問題就是了。

參考:
1. https://learn.microsoft.com/zh-tw/dotnet/core/extensions/workers
2. https://blog.darkthread.net/blog/net6-windows-service/
3. https://stackoverflow.com/questions/70573453/how-can-i-use-configuration-in-net-core-6-worker-app
4. https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service?WT.mc_id=DOP-MVP-37580

Comments

No comments yet. Why don’t you start the discussion?

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料