(此文章同時發表在本人微信公眾號“dotNET開發經驗談”,歡迎右邊二維碼來關注。)
題記:正在構思一個中間件的設計,考慮是否既可以使用最新的技術,也可以兼顧傳統的部署模式。所以有了這個問題(包括衍生問題)的提出和解決方法。
托管到Windows Service中
眾所周知,ASP.NET Core采用了和傳統ASP.NET不同的托管和HTTP處理方式,即把服務器和托管環境完全解耦。
ASP.NET Core內置了兩個HTTP服務器實現,一個是基於libuv實現的Kestrel(支持跨平台),一個是基於Windows HTTP Server API實現的WebListener(僅支持Windows)。
而托管環境可以和服務器不相關,一般情況是自托管,或者托管到IIS/IISExpress中(此處的IIS僅作為反向代理把請求轉發給所使用的服務器實現)。
因此,打算以Windows Service這種比較傳統的方式來部署ASP.NET Core的Web應用也是可行的(本質還是自托管,只是啟動進程並非控制台程序,而是一個Windows Service)。這不,微軟就很貼心的提供了一個Nuget來支持:Microsoft.AspNetCore.Hosting.WindowsServices,它的源碼在:https://github.com/aspnet/Hosting/tree/dev/src/Microsoft.AspNetCore.Hosting.WindowsServices。
使用它也很簡單:
- 創建一個以.NET Framework為運行時的ASP.NET Core應用,即模版選擇“ASP.NET Core Web Application (.NET Framework)”。
- 引用Microsoft.AspNetCore.Hosting.WindowsServices。
- 在Program的Main方法中,把默認的host.Run改為host.RunAsService。
- 編譯程序后,會在Debug目錄下看到你選用的運行時版本的一個目錄,比如“net46”,在里面會看到編譯好的exe文件和一個類似“win7-x64”的這樣文件夾。
- 進入到“win7-x64”文件夾,在命令行執行“sc create MyService binPath = "Full\Path\To\The\Console\file.exe"”,來創建一個Windows Service。注意:binPath必須是全路徑。
- 這樣就可以在Windows Service中托管ASP.NET Core應用了。
- 如果希望在服務啟動和停止的過程中做一些額外處理,比如記錄日志,那么可以實現一個CustomWebHostService來繼承
WebHostService
,並在其中編寫所需的代碼。 - 並實現如下的擴展方法:
public static class CustomWebHostWindowsServiceExtensions { public static void RunAsCustomService(this IWebHost host) { var webHostService = new CustomWebHostService(host); ServiceBase.Run(webHostService); } } host.RunAsCustomService();
把ASP.NET Core應用托管到Windows Service中,就這么簡單!
更多問題?
不過,我想從我的場景來談談為什么我有托管到Windows Service的需求。這幾天在構思一個中間件(包含多個組件)的架構,考慮到初期會以比較傳統的方式來部署,后期有可能跨平台,並且希望組件之間能夠相對獨立和解耦。所以,最自然的想法就是架構設計為微服務,基於ASP.NET Core實現(未來不排除使用其他技術棧)。部署的話,初期分離部署為多個Windows Service,后期也可以很平滑的過度到容器或者類似Service Fabric這樣的微服務運行平台中。
基於這樣的設計考慮,要解決的第一個問題就是是否可以把ASP.NET Core應用托管到Windows Service中(上面已經驗證了),第二個問題是是否可以根據環境條件跑在不同的啟動進程中,第三問題是是否可以同時支持多種運行時。2,3個問題要解決其實也非常簡單。
支持不同啟動方式
第二個問題的解決辦法如下:
- 在Program的Main方法中,判斷一下特定的命令行參數,比如“--windows-service”
- 以這個參數啟動的情況下,就host.RunAsService,不是的話就host.Run。
就是這么簡單粗暴。
支持不同運行時
.NET Core本來就支持一個項目多個運行時,就算把net46和netcoreapp1.0混合也是可以的。具體方法如下:
- 修改project.json文件,在frameworks下額外添加“netcoreapp1.0”。
- 把對Microsoft.AspNetCore.Hosting.WindowsServices的依賴移到net46下
- 在netcoreapp1.0下添加“Microsoft.NETCore.App”的依賴
- 在Program的Main方法中,基於條件編譯的符號來判斷不同的運行時,具體的符號表見:https://docs.microsoft.com/en-us/dotnet/articles/core/tutorials/libraries#how-to-multitarget
示例源代碼
為了避免在文章中貼大段的源代碼,大家轉到GitHub中去看示例代碼吧:https://github.com/heavenwing/HostingAspCoreAsWindowsService