當咱們將原有ASP.NET 應用程序升級遷移到ASP.NET Core以後,咱們發現代碼工程中多了兩個類Program類和Startup類。git
接下來咱們詳細探祕一下通用主機Host的啓動過程。github
1、Program類的Main函數入口web
Program類最重要的功能就是啓動主機,這裏有一個主機的概念,是ASP.NET Core全新引入的。json
主機負責應用程序啓動和生存期管理。 同時,主機也是封裝應用程序資源的對象:windows
啓動主機時,它在 DI 容器中找到 IHostedService 的每一個實現,而後調用 IHostedService.StartAsync。 在 web 應用中,其中一個 IHostedService 的實現是啓動 HTTP 服務器實現的 web 服務。這裏的HTTP服務器默認是Kestrel。服務器
即:ASP.NET Core主機啓動時,會啓動一個HTTP服務器,默認是Kestrel。啓動後監聽並響應某個端口的HTTP請求。app
咱們繼續看Program類的代碼: ide
從上述代碼能夠看到,Main函數中首先調用CreateHostBuilder方法,返回一個IHostBuilder。而後調用IHostBuilder.Build()方法完成函數
2、Host.CreateDefaultBuilder(args): 構造IHostBuilder的默認實現HostBuilderoop
在CreateHostBuilder方法內部,首先調用了Host.CreateDefaultBuilder構造了一個HostBuilder,這個咱們先看下源碼,看看到底Host類內部作了什麼操做:
https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/Host.cs
public static IHostBuilder CreateDefaultBuilder(string[] args) { var builder = new HostBuilder(); builder.UseContentRoot(Directory.GetCurrentDirectory()); builder.ConfigureHostConfiguration(config => { config.AddEnvironmentVariables(prefix: "DOTNET_"); if (args != null) { config.AddCommandLine(args); } }); builder.ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment; config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName)) { var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName)); if (appAssembly != null) { config.AddUserSecrets(appAssembly, optional: true); } } config.AddEnvironmentVariables(); if (args != null) { config.AddCommandLine(args); } }) .ConfigureLogging((hostingContext, logging) => { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); // IMPORTANT: This needs to be added *before* configuration is loaded, this lets // the defaults be overridden by the configuration. if (isWindows) { // Default the EventLogLoggerProvider to warning or above logging.AddFilter<EventLogLoggerProvider>(level => level >= LogLevel.Warning); } logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); logging.AddEventSourceLogger(); if (isWindows) { // Add the EventLogLoggerProvider on windows machines logging.AddEventLog(); } }) .UseDefaultServiceProvider((context, options) => { var isDevelopment = context.HostingEnvironment.IsDevelopment(); options.ValidateScopes = isDevelopment; options.ValidateOnBuild = isDevelopment; }); return builder; }
從上述代碼中,能夠看到CreateDefaultBuilder內部構造了一個HostBuilder,同時設置了:
以上構造完成了HostBuilder,針對ASP.NET Core應用,代碼繼續調用了HostBuilder.ConfigureWebHostDefaults方法。
3、IHostBuilder.ConfigureWebHostDefaults:經過GenericWebHostBuilder對HostBuilder增長ASP.NET Core的運行時設置
構造完成HostBuilder以後,針對ASP.NET Core應用,繼續調用了HostBuilder.ConfigureWebHostDefaults方法。這是一個ASP.NET Core的一個擴展方法:
咱們繼續看ConfigureWebHostDefaults擴展方法內部作了哪些事情:
using System; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore; namespace Microsoft.Extensions.Hosting { /// <summary> /// Extension methods for configuring the IWebHostBuilder. /// </summary> public static class GenericHostBuilderExtensions { /// <summary> /// Initializes a new instance of the <see cref="IWebHostBuilder"/> class with pre-configured defaults. /// </summary> /// <remarks> /// The following defaults are applied to the <see cref="IWebHostBuilder"/>: /// use Kestrel as the web server and configure it using the application's configuration providers, /// adds the HostFiltering middleware, /// adds the ForwardedHeaders middleware if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true, /// and enable IIS integration. /// </remarks> /// <param name="builder">The <see cref="IHostBuilder" /> instance to configure</param> /// <param name="configure">The configure callback</param> /// <returns>The <see cref="IHostBuilder"/> for chaining.</returns> public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure) { return builder.ConfigureWebHost(webHostBuilder => { WebHost.ConfigureWebDefaults(webHostBuilder); configure(webHostBuilder); }); } } } © 2020 GitHub, Inc.
首先,經過類GenericHostWebHostBuilderExtensions,對IHostBuilder擴展一個方法:ConfigureWebHost:builder.ConfigureWebHost
在這個擴展方法中實現了對IWebHostBuilder的依賴注入:即將GenericWebHostBuilder實例傳入方法ConfigureWebHostDefaults內部
using System; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.Extensions.Hosting { public static class GenericHostWebHostBuilderExtensions { public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure) { var webhostBuilder = new GenericWebHostBuilder(builder); configure(webhostBuilder); builder.ConfigureServices((context, services) => services.AddHostedService<GenericWebHostService>()); return builder; } } }
經過GenericWebHostBuilder的構造函數GenericWebHostBuilder(buillder),將已有的HostBuilder增長了ASP.NET Core運行時設置。
。。。
先看到這,讓咱們回到ConfigureWebHostDefaults:
將上面兩段代碼合併一下進行理解:ConfigureWebHostDefaults作了兩件事情:
1. 擴展IHostBuilder增長ConfigureWebHost,引入IWebHostBuilder的實現GenericWebHostBuilder,將已有的HostBuilder增長ASP.NET Core運行時的設置。
2. ConfigureWebHost代碼中的configure(webhostBuilder):對注入的IWebHostBuilder,調用 WebHost.ConfigureWebDefaults(webHostBuilder),啓用各種設置,以下代碼解讀:
internal static void ConfigureWebDefaults(IWebHostBuilder builder) { builder.ConfigureAppConfiguration((ctx, cb) => { if (ctx.HostingEnvironment.IsDevelopment()) { StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration); } }); builder.UseKestrel((builderContext, options) => { options.Configure(builderContext.Configuration.GetSection("Kestrel")); }) .ConfigureServices((hostingContext, services) => { // Fallback services.PostConfigure<HostFilteringOptions>(options => { if (options.AllowedHosts == null || options.AllowedHosts.Count == 0) { // "AllowedHosts": "localhost;127.0.0.1;[::1]" var hosts = hostingContext.Configuration["AllowedHosts"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); // Fall back to "*" to disable. options.AllowedHosts = (hosts?.Length > 0 ? hosts : new[] { "*" }); } }); // Change notification services.AddSingleton<IOptionsChangeTokenSource<HostFilteringOptions>>( new ConfigurationChangeTokenSource<HostFilteringOptions>(hostingContext.Configuration)); services.AddTransient<IStartupFilter, HostFilteringStartupFilter>(); if (string.Equals("true", hostingContext.Configuration["ForwardedHeaders_Enabled"], StringComparison.OrdinalIgnoreCase)) { services.Configure<ForwardedHeadersOptions>(options => { options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; // Only loopback proxies are allowed by default. Clear that restriction because forwarders are // being enabled by explicit configuration. options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>(); } services.AddRouting(); }) .UseIIS() .UseIISIntegration(); }
內部實現了:
3. 返回ConfigureWebHostDefaults代碼中的configure(webHostBuilder):執行Program類中的webBuilder.UseStartup<Startup>();
第三章節中,以上過程完成了IHostBuilder.ConfigureWebHostDefaults,經過GenericWebHostBuilder對HostBuilder增長ASP.NET Core的運行時設置。
接下來繼續Build和Run的過程。
4、CreateHostBuilder(args).Build().Run();
CreateHostBuilder返回的IHostBuilder,咱們經過代碼Debug,看一下具體的類型:Microsoft.Extensions.Hosting.HostBuilder,這樣進一步驗證了前三個章節的代碼。
1. Build的過程
先看下Build的源碼:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/HostBuilder.cs
Build的過程主要完成了:
2. Run的過程
咱們先經過Debug,看一下Host的信息:Microsoft.Extensions.Hosting.Internal.Host
這個Run方法也是一個擴展方法:HostingAbstractionsHostExtensions.Run
其實內部轉調的仍是Host.StartAsync方法,在內部啓動了DI依賴注入容器中全部註冊的服務。
代碼連接:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/Internal/Host.cs
整個Host主機的啓動過程仍是很是複雜的,咱們只是簡單的在代碼層面研究了一遍,感受只是有了個大體的輪廓,具體怎麼執行的,是否是如上面代碼的解釋,還須要深刻繼續研究。
接下來下一篇文章準備把源碼單步調試看看。加深對ASP.NET Core底層技術原理的理解,只有理解了底層技術實現,咱們在應用層才能更好、正確的使用。
周國慶
2020/4/6