如何託管ASP.NET Core應用到Windows Service中

(此文章同時發表在本人微信公衆號「dotNET開發經驗談」,歡迎右邊二維碼來關注。)git

題記:正在構思一箇中間件的設計,考慮是否既可使用最新的技術,也能夠兼顧傳統的部署模式。因此有了這個問題(包括衍生問題)的提出和解決方法。github

託管到Windows Service中

衆所周知,ASP.NET Core採用了和傳統ASP.NET不一樣的託管和HTTP處理方式,即把服務器和託管環境徹底解耦。web

ASP.NET Core內置了兩個HTTP服務器實現,一個是基於libuv實現的Kestrel(支持跨平臺),一個是基於Windows HTTP Server API實現的WebListener(僅支持Windows)。json

而託管環境能夠和服務器不相關,通常狀況是自託管,或者託管到IIS/IISExpress中(此處的IIS僅做爲反向代理把請求轉發給所使用的服務器實現)。windows

所以,打算以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服務器

使用它也很簡單:微信

  1. 建立一個以.NET Framework爲運行時的ASP.NET Core應用,即模版選擇「ASP.NET Core Web Application (.NET Framework)」。
  2. 引用Microsoft.AspNetCore.Hosting.WindowsServices。
  3. 在Program的Main方法中,把默認的host.Run改成host.RunAsService。
  4. 編譯程序後,會在Debug目錄下看到你選用的運行時版本的一個目錄,好比「net46」,在裏面會看到編譯好的exe文件和一個相似「win7-x64」的這樣文件夾。
  5. 進入到「win7-x64」文件夾,在命令行執行「sc create MyService binPath = "Full\Path\To\The\Console\file.exe"」,來建立一個Windows Service。注意:binPath必須是全路徑。
  6. 這樣就能夠在Windows Service中託管ASP.NET Core應用了。
  7. 若是但願在服務啓動和中止的過程當中作一些額外處理,好比記錄日誌,那麼能夠實現一個CustomWebHostService來繼承WebHostService ,並在其中編寫所需的代碼。
  8. 並實現以下的擴展方法:
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這樣的微服務運行平臺中。app

基於這樣的設計考慮,要解決的第一個問題就是是否能夠把ASP.NET Core應用託管到Windows Service中(上面已經驗證了),第二個問題是是否能夠根據環境條件跑在不一樣的啓動進程中,第三問題是是否能夠同時支持多種運行時。2,3個問題要解決其實也很是簡單。微服務

支持不一樣啓動方式

第二個問題的解決辦法以下:

  1. 在Program的Main方法中,判斷一下特定的命令行參數,好比「--windows-service」
  2. 以這個參數啓動的狀況下,就host.RunAsService,不是的話就host.Run。

就是這麼簡單粗暴。

支持不一樣運行時

.NET Core原本就支持一個項目多個運行時,就算把net46和netcoreapp1.0混合也是能夠的。具體方法以下:

  1. 修改project.json文件,在frameworks下額外添加「netcoreapp1.0」。
  2. 把對Microsoft.AspNetCore.Hosting.WindowsServices的依賴移到net46下
  3. 在netcoreapp1.0下添加「Microsoft.NETCore.App」的依賴
  4. 在Program的Main方法中,基於條件編譯的符號來判斷不一樣的運行時,具體的符號表見:https://docs.microsoft.com/en-us/dotnet/articles/core/tutorials/libraries#how-to-multitarget

示例源代碼

爲了不在文章中貼大段的源代碼,你們轉到GitHub中去看示例代碼吧:https://github.com/heavenwing/HostingAspCoreAsWindowsService

相關文章
相關標籤/搜索