使用.NET Core建立Windows服務
原文:Creating Windows Services In .NET Core – Part 1 – The “Microsoft” Way
作者:Dotnet Core Tutorials
譯者:Lamond Lu
譯文:使用.NET Core建立Windows服務(一) - 使用官方推薦方式
建立Windows服務來執行批處理任務或者執行後臺任務,是一種非常常見的模式,但是由於雲服務(Amazon Lambda, Azure WebJobs以及Azure Functions)的激增,你可能不會經常使用Windows服務了。個人而言,我非常喜歡使用Azure WebJobs, 因為我可以直接編寫一個控制檯程式,而不需要考慮如何雲中執行它,一個批處理檔案可以將其裝換成一個自動化任務,並且可以保證7*24小時的執行。
但是也許你還沒有使用雲服務,或者你有一堆要作為Windows服務執行的舊版應用程式需要轉換為.NET Core, 但是不能完全將他們轉換為“無伺服器”(serverless)應用。 那麼這邊文章就是適合你的。
在許多方面,.NET Core中的Windows服務和.NET Framework中的Windows服務完全相同。但是,在編寫服務的時候,你可能會遇到一些小問題。此外,本文中,我們僅介紹“Microsoft”方式的Windows服務建立,在後續,我會繼續介紹如何使用第三方庫TopShelf
來簡化這該過程。
安裝
由於Visual Studio沒有提供建立Windows服務的模板,所以我們需要通過建立控制檯程式的方式來建立一個Windows服務。
建立完成之後,我們需要安裝一個Nuget程式包,這個程式包會將一些Windows特定的API新增到.NET Core中,這些API實際上已經在完整框架中提供了,但是其中許多是Windows特有的,例如Windows服務。因此, 它們並沒有包含在.NET Core的基礎庫中,但是可以通過將Nuget程式包的方式引入到.NET Core中。
下面我們就可以在Package Manager Console中輸入以下命令。
Install-Package Microsoft.Windows.Compatibility
程式碼
以上引入的Nuget程式包中,最讓我們感興趣的是ServiceBase
類。這是一個用於編寫Windows服務的基類,它提供了一系列的事件鉤子,包含服務啟動、結束、暫停等。
下面呢,我們將在程式碼中建立一個類,這個類負責將一些簡單的日誌輸出到一個臨時檔案中。我們將使用這個例子來了解其中的原理。我們的程式碼如下:
class LoggingService : ServiceBase { private const string _logFileLocation = @"C:\temp\servicelog.txt"; private void Log(string logMessage) { Directory.CreateDirectory(Path.GetDirectoryName(_logFileLocation)); File.AppendAllText(_logFileLocation, DateTime.UtcNow.ToString() + " : " + logMessage + Environment.NewLine); } protected override void OnStart(string[] args) { Log("Starting"); base.OnStart(args); } protected override void OnStop() { Log("Stopping"); base.OnStop(); } protected override void OnPause() { Log("Pausing"); base.OnPause(); } }
所以這裡你會注意到,我們的類是繼承了ServiceBase
類,並且我們重寫了幾個事件方法,輸出了一些日誌。在服務啟動時,會觸發OnStart
事件,在服務終止的時候,會觸發OnStop
事件。這裡我們不應該將過於繁重的任務放置在OnStart
事件中來處理。
如果我們想從Main
方式中啟動這個服務,程式碼非常的簡單。
static void Main(string[] args) { ServiceBase.Run(new LoggingService()); }
以上就是全部程式碼。
服務部署
在釋出服務的時候,我們不可能僅依靠Visual Studio來構建我們所需要的服務,我們還需要專門針對Windows執行時進行構建。為此,我們需要在專案根目錄的命令提示符下執行以下命令。注意,這裡我們傳入了一個-r
標記來告訴它要構建那個平臺。
dotnet publish -r win-x64 -c Release
命令執行完畢之後,我們可以檢查以下/bin/release/netcoreappX.X/publish
目錄,我們可以找到所有的釋出程式碼,但是最重要的是,這裡我們可以得到一個可執行的exe檔案。如果我們不指定執行時,我們只會獲得一個.NET Core的dll程式集,使用這個程式集,我們是沒有辦法建立Windows服務的。
現在我們可以將這個釋出目錄移動帶其他的任何地方,但是現在我們就暫時使用當前的釋出目錄。
下一步,我們需要使用管理員角色開啟一個命令提示符,然後輸入一下命令。
sc create TestService BinPath=C:\full\path\to\publish\dir\WindowsServiceExample.exe
SC
命令是一個標準的Windows命令(與.NET Core無關),它可以用來安裝Windows服務。這裡我們將我們的測試服務命名為TestService
,更重要的是,我們通過BinPath
引數指定了可執行exe檔案。
執行之後,我們應該會得到以下結果。
[SC] CreateService SUCCESS
然後我們要做的就是啟動服務。
sc start TestService
現在我們可以檢視一下我們的日誌檔案,檢視服務的執行情況。
如果想要停止並刪除服務,我們可以使用一下命令。
sc stop TestService
sc delete TestService
服務除錯
在這裡,我真的認為,使用"Microsoft"的方式註定會失敗。因為除錯服務實在是太繁瑣了。
首先,我們將ServiceBase
中重寫的方法設定為受保護,這意味著我們無法在類之外訪問它們,這使得除錯它們變得更加困難。這裡我發現最好的方法是為每個事件提供一個public方法, 並在受保護方法中呼叫這些public方法來完成功能,這雖然有點混亂,
public void OnStartPublic(string[] args) { Log("Starting"); } protected override void OnStart(string[] args) { OnStartPublic(args); base.OnStart(args); }
但是至少我們可以做如下了事情了。
static void Main(string[] args) { var loggingService = new LoggingService(); if (true) //Some check to see if we are in debug mode (Either #IF Debug etc or an app setting) { loggingService.OnStartPublic(new string[0]); while(true) { //Just spin wait here. Thread.Sleep(1000); } //Call stop here etc. } else { ServiceBase.Run(new LoggingService()); } }
你的另一個選擇是,在除錯模式下進行專案釋出,安裝服務,然後附加偵錯程式。實際上,這是Microsoft建議你使用的方式,但是我認為這簡直一團糟。
後續
實際上,我們可以在這裡做一些其他非常有用的事情, 比如我們可以通過建立一個install.bat批處理檔案來為我們執行SC Create命令。但我認為,上面我們看到的除錯問題,已經讓我不再想使用這種方式了。 幸運的是,有一個名為Topshelf
的庫可以幫助我們減輕很多麻煩,在本系列的下一部分中,我們將研究如何它。
*博主的文章是自己平時開發總結的經驗,由於博主的水平不高,不足和錯誤之處在所難免,希望大家能夠批評指出。
*我的部落格: http://www.cnblogs.com/lxhbky/