ASP.NET Core 启动方式(Hosting)
之前版本的ASP.NET程序必須依賴IIS來啟動,而IIS上會為掛載在其中的ASP.NET 注冊一個ISAPI filter。每當(dāng)http請求過來時,IIS則會啟動w3wp的worker process來開始整個ASP.NET runtime程序。相信大家都這樣的流程都有相應(yīng)的了解。在.net core之前,ASP.NET的主要場景都是運行在Windows平臺的,IIS也就是web server的首選了。雖然也有類似于jexus的linux webserver可用,但是基于mono的.NET 總體還是不夠Microsoft 原生的強。
不過到了現(xiàn)在,一切都不同了。
新版ASP.NET Core有了.NET Core的支援后已經(jīng)開始了它的跨平臺之旅了,因此ASP.NET Core的啟動方式也得開始重新設(shè)計以適應(yīng)新需求了。
1、Kestrel 和 IIS platform handler
在 ASP.NET Core 中,整個runtime都是重寫過的,所以它和IIS之間的關(guān)系也有所改變。而ASP.NET Core為了跨平臺,它現(xiàn)在的執(zhí)行方式就如一般的Console app一樣。ASP.NET Core自帶一個高性能的I/O組件 - Kestrel,使得它可以不依賴IIS的存在便啟動了runtime。不過Kestrel 也只是一個I/O組件,并沒有想IIS提供其它的功能來保護(hù)和管理ASP.NET 應(yīng)用程序。ASP.NET Core同樣可以通過IIS進(jìn)行處理。但是如果通過IIS來進(jìn)行處理的話,這個時候我們便需要一個“中間人”的角色來負(fù)責(zé)這個功能了。這個“中間人”的名字叫 Http Platform Handler,主要表現(xiàn)在web.config文檔中的設(shè)置,其中包括啟動ASP.NET Core 程序的的路徑和名稱,需要傳入的參數(shù)以及一些其他的設(shè)置選項。Http Platform Handler的具體設(shè)置例子如下:
<system.webServer> <handlers> <add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/> </handlers> <httpPlatform processPath="WebApp.exe" arguments="" stdoutLogEnabled="false" startupTimeLimit="3600"/> </system.webServer> |
關(guān)于Http Platform Handler的相關(guān)資料可以看這個鏈接:
http://www.iis.net/downloads/microsoft/httpplatformhandler
從上面的例子可以看出來,ASP.NET Core編譯之后便是一個EXE程序,使得你可以直接運行。因此,當(dāng)HTTP請求進(jìn)來時,IIS先接受請求,然后根據(jù)你設(shè)置的web.config的內(nèi)容將請求轉(zhuǎn)發(fā)給WebApp.exe(你的ASP.NET Core程序),然后WebApp.exe開始執(zhí)行時便會啟動Kestrel,接著這個HTTP請求便進(jìn)入了ASP.NET Core runtime的世界。這樣看來,IIS這時候只是一個簡單的proxy/forwarder角色。
PS:在我翻譯整理這個文章的時候,世界已經(jīng)發(fā)生了變化:下個版本的asp.net core 將會有全新的IIS module來取代Http Platform Handler。
具體詳細(xì)資料見:https://github.com/aspnet/IISIntegration/issues/105
2、Main()
ASP.NET Core和其他的.NET 程序一樣擁有一個static void Main(),這是整個runtime的進(jìn)入點。下面看一個樣例:
public static void Main(string[] args) { var host = new WebHostBuilder() .UseServer("Microsoft.AspNetCore.Server.Kestrel") .UseContentRoot(Directory.GetCurrentDirectory()) .UseDefaultConfiguration(args) .UseIISPlatformHandlerUrl() .UseStartup<Startup>() .Build(); host.Run(); } |
ASP.NET Core host engine 的建立者是 WebHostBuilder.
它實質(zhì)上是一個了 IWebHostBuilder interface,其中 UseServer() 是用于指定使用什么樣的 server。
其中 UseServer() 有一個擴(kuò)展方法UseServer(string assemblyName)。這樣的話我們可以直接傳入Kestrel的程序集名稱:”Microsoft.AspNetCore.Server.Kestrel”。當(dāng)然,這只是其中一種選項。你也可以自己實現(xiàn)一個自己的 server,只要你的 server 實現(xiàn)了 IServerFactory interface 即可。這樣的設(shè)計提供了一個很大的彈性空間讓我們自行選擇hosting server(托管服務(wù))。
UseContentRoot()
這個擴(kuò)展方法是讓我們指定應(yīng)用程序的工作目錄(working directory),如果我們沒有指定的話,則會默認(rèn)為我們的應(yīng)用(webapp.exe)所在目錄為工作目錄。
UseDefaultConfiguration()
這個擴(kuò)展方法使得我們在IWebHostBuilder 建立可以傳入一些參數(shù),比如 application key, environment name, server factory location, content root path 等等.因此,當(dāng)我們在運行 WebApp.exe 的時候,同時可以帶入我們需要用到的hosting參數(shù)(PS:這樣的做法就像運行命令行程序時帶入?yún)?shù),多好玩)。這些參數(shù)也可以寫在appsettings.json里面通過Configuration來讀取。
所以,UseDefaultConfiguration() 也不一定非要存在于 Main() 之中。(???個人不是很理解)
PS:原作者原話,”如果我沒記錯的話,在寫這個文章的時候,UseDefaultConfiguration() 已經(jīng)被改為成了UseDefaultHostingConfiguration().顯然這個名稱更能清楚明白.”(???個人還沒實踐)
UseIISPlatofmrHandleUrl()
這個 IWebHostBuilder 的 擴(kuò)展方法比較特殊。如果你要把 ASP.NET Core 放在 IIS 下,這個擴(kuò)展方法會讀取 IIS http platform handler 的 server port 和 application path,用于作為 ASP.NET Core 的啟動位置,如http://localhost:5000/start.如果你沒用?IIS,這個擴(kuò)展方法對你來說基本是用不上的.
UseStartup<>()
這是 WebHostBuilder 里相當(dāng)重要的一個擴(kuò)展方法。它的方法簽名如下:
1 2 3 | public static IWebHostBuilder UseStartup<TStartup> (this IWebHostBuilder hostBuilder) where TStartup : class |
這里你可以很清楚地看到 <> 里面要放的就是一個 class。
在我們這里的范例中,它的名字是 Startup,里面最重要的就是需要定義要使用那些服務(wù)(service)以及要使用那些中間件(middleware)。
3、Startup
這是一個非常非常重要的class,在ASP.NET Core范例中一般都把它命名成Startup。其實我們把它命名成其他名字也是可以的,或者設(shè)定多個Startup。上面的內(nèi)容可以看到,UseStartup()指定了誰是starup class。然后在Build()便會實例化starup class,之后便執(zhí)行里面兩個重要的方法:ConfigureServices() 和 Configure()。
我們先來看 Startup 的構(gòu)造函數(shù).
public Startup(IHostingEnvironment env) { // Set up configuration sources. var builder = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build().ReloadOnChanged("appsettings.json"); } |
Host engine 在被執(zhí)行 Build()時已經(jīng)知道 startup type 是 Startup class,所以在 Build() 的時候會先創(chuàng)建期示例。在我們這里的是調(diào)用Startup類帶參數(shù)的構(gòu)造函數(shù)。
在我們這個例子中選擇的是傳入IHostingEnvironment示例,它為我們帶來了環(huán)境變量(EnvironmentName)。
我們這里主要目的是把Configuration實例化。這是一個蠻重要的基礎(chǔ)組件,以后會有文章來說明它。
在這里我們特別說明一下,上面的示例代碼中,我們執(zhí)行了兩次 AddJsonFile(),而且第二個json file的參數(shù)和第一個的還不太一樣。這樣的目的是為了讓開發(fā)者可以把開發(fā)環(huán)境使用的環(huán)境參數(shù)和其他環(huán)境使用的參數(shù)有所區(qū)別。比如,你使用的開發(fā)環(huán)境用的是appsettings.json,這個文件只存在于你的電腦中。另一個文檔是appsettings.production.json,這是正式環(huán)境使用的參數(shù)設(shè)定文檔。第二個 AddJsonFile() 第二個參數(shù)是 true,也就是可能不存在的意思。所以若遇到重復(fù)名稱參數(shù)時,appsettings.production.json 會覆蓋 appsettings.json 的內(nèi)容。這樣使得開發(fā)環(huán)境和生產(chǎn)環(huán)境得以區(qū)分。
接下來,在 IWebHostBuilder 的 Build()里面會執(zhí)行 host engine 初始化的程序,其中就會去找Startup class里面的兩個方法: ConfigureServices() 和 Configure()。
ConfigureSerivces() 是定義了這個 web application 要使用那些服務(wù),然后將這些服務(wù)放在 service container (IServiceCollection) 裡面。如下面的樣例:
public void ConfigureServices(IServiceCollection services) { // add entity framework services.AddEntityFramework() .AddDbContext<BlogsContext>(o => o.UseSqlServer(Configuration["Data1:DefaultConnection:ConnectionString"])) // Add framework services. services.AddMvc(); } |
它定義了entity framework和mvc兩個服務(wù)。這里所謂的服務(wù)(services)的意思也就是通過它們帶入更龐大的程序代碼。這聽起來好像有點搞笑,但也真的如此。像Entity framework 里面有這么多的代碼,一定都需要帶入許多定義好的物件或者參數(shù),而不只是一個程序的進(jìn)去點而已,所以services 的目的就是在這里。
Configure() 主要是定義了中間件(middleware)以及它們的順序.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseStaticFiles(); app.UseMvcWithDefaultRoute(); } |
4、Build 和 Run
最后,在IHostWebBuilder里最后的兩個動作便是:Build and Run.
Build()
這個方法做的工作便是建立 hosting service,把 Startup 中定義的的 services 和 middleware 接收過來,然后確定content root path 和 application name,接著一句前面這些資料再加上Configuration過來的數(shù)據(jù)來初始化host engine (WebHost.cs).
Run()
這個是啟動 host engine 的 擴(kuò)展方法,它在啟動之前加入了一個 CancelKeyPress 的事件.因為在 Run() 方法 中傳入入了 CancellationTokenSource() ,讓我們有一個方法可以隨時中斷host engine的執(zhí)行。
目前的做法就用是 CancelKeyPress 事件,所以你可以按下 Ctrl+C 來中止 host engine 的執(zhí)行.
比較特別的是,這一段中止的文字說明居然是用 hard code,參考如下:
host.Run(cts.Token, “Application started. Press Ctrl+C to shut down.”);
不過這樣的話,這里你也不能寫中文…
本文整理于https://dotblogs.com.tw/aspnetshare/2016/03/28/20160327并已征得作者同意。
感謝Bruce的分享。
原文鏈接:http://codelover.link/2016/07/28/asp.net-core-startup/
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的ASP.NET Core 启动方式(Hosting)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10月15日 2016中国开源年会期待您
- 下一篇: .NET CoreCLR开发人员指南(上