.NET Core技术研究-主机
前一段時間,和大家分享了?ASP.NET Core技術研究-探秘Host主機啟動過程
但是沒有深入說明主機的設計。今天整理了一下主機的一些知識,結合先前的博文,完整地介紹一下.NET Core的主機的設計和構建啟動過程。
一、什么是主機
? 主機是一個封裝了應用資源的對象,即:主機封裝了一堆應用資源,封裝了哪些應用資源呢?
依賴注入框架 DI?
Logging日志
Configuration 配置
托管服務:IHostedService服務接口的實現
二、Web主機和通用主機
? ? 先說Web主機:即ASP.NET Core Web主機,概括的講就是托管Web程序的Host。在低于 3.0 的 ASP.NET Core 版本中,Web 主機用于 HTTP 工作負載。
? ? 我們新建一個ASP.NET Core2.2的Web應用程序,在Program類的Main函數中我們可以看到整個WebHost的構造、啟動過程:
? ??
? ??
? ?.NET Core提供Web主機的同時,還提供了一個通用主機的概念。
? ?通用主機Host和Web主機提供了類似的架構和功能,包含依賴注入框架DI、日志、配置、各類應用(托管服務)。通用主機的出現,給了我們更多開發的選擇,比如說后臺處理任務場景。
? ?在.NET Core3.1版本后,微軟不再建議將 Web 主機用于 Web 應用,直接使用Host通用主機來替換WebHost,
? ?一句話:通用主機可以托管任何類型的應用,包括 Web 應用。通用主機將替換 Web 主機。為了向下兼容,WebHost依然可以使用。
? ??我們新建一個ASP.NET Core3.1的Web應用程序,在Program類的Main函數中我們可以看到整個WebHost的構造、啟動過程:
? ??
? ?接下來,我們將以ASP.NET Core 3.1這個版本,介紹一下主機的構建過程和啟動過程
三、主機是如何構建的
? ?從上述代碼可以看到,Main函數中首先調用CreateHostBuilder方法,返回一個IHostBuilder。然后調用IHostBuilder.Build()方法完成
? 1. 通過Host.CreateDefaultBuilder(args): 構造IHostBuilder的默認實現HostBuilder
???在CreateHostBuilder方法內部,首先調用了Host.CreateDefaultBuilder構造了一個HostBuilder,這個我們先看下源碼,看看到底Host類內部做了什么操作:
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 abovelogging.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 machineslogging.AddEventLog();}}).UseDefaultServiceProvider((context, options) =>{var isDevelopment = context.HostingEnvironment.IsDevelopment();options.ValidateScopes = isDevelopment;options.ValidateOnBuild = isDevelopment;});return builder;}
從上述.NET Core源代碼中,可以看到CreateDefaultBuilder內部構造了一個HostBuilder,同時設置了:
將內容根目錄(contentRootPath)設置為由 GetCurrentDirectory 返回的路徑。
通過以下源加載主機配置
環境變量(DOTNET_前綴)配置
命令行參數配置
? ? ?通過以下對象加載應用配置
appsettings.json?
appsettings.{Environment}.json
密鑰管理器 當應用在 Development 環境中運行時
環境變量
命令行參數
? ? ?添加日志記錄提供程序
控制臺
調試
EventSource
EventLog( Windows環境下)
當環境為“開發”時,啟用范圍驗證和依賴關系驗證。
? ?以上構造完成了HostBuilder,針對ASP.NET Core應用,代碼繼續調用了HostBuilder.ConfigureWebHostDefaults方法。
? ?2. IHostBuilder.ConfigureWebHostDefaults:通過GenericWebHostBuilder對HostBuilder增加ASP.NET Core的運行時設置
? ?構造完成HostBuilder之后,針對ASP.NET Core應用,繼續調用了HostBuilder.ConfigureWebHostDefaults方法。這是一個ASP.NET Core的一個擴展方法:
? ?
? ?我們繼續看ConfigureWebHostDefaults擴展方法內部做了哪些事情:
? ?ASP.NET Core源碼連接:https://github.com/dotnet/aspnetcore/blob/master/src/DefaultBuilder/src/GenericHostBuilderExtensions.cs ?? ??
? ?
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);});}} }首先,通過類GenericHostWebHostBuilderExtensions,對IHostBuilder擴展一個方法:ConfigureWebHost:builder.ConfigureWebHost
? ? ?在這個擴展方法中實現了對IWebHostBuilder的依賴注入:即將GenericWebHostBuilder實例傳入方法ConfigureWebHostDefaults內部
? ? ?代碼連接:https://github.com/dotnet/aspnetcore/blob/release/3.1/src/Hosting/Hosting/src/GenericHostWebHostBuilderExtensions.cs ???
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運行時設置。
? ?可以參考ASP.NET Core源代碼:https://github.com/dotnet/aspnetcore/blob/release/3.1/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs
? ?先看到這,讓我們回到ConfigureWebHostDefaults:
? ?將上面兩段代碼合并一下進行理解:ConfigureWebHostDefaults做了兩件事情:
? ?①.?擴展IHostBuilder增加ConfigureWebHost,引入IWebHostBuilder的實現GenericWebHostBuilder,將已有的HostBuilder增加ASP.NET Core運行時的設置。
? ?②??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) =>{// Fallbackservices.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 notificationservices.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();}其內部實現了:
前綴為 ASPNETCORE_ 的環境變量加載主機配置。
將?Kestrel作為默認的Web服務器
添加HostFiltering中間件(主機篩選中間件)
如果ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,添加轉接頭中間件ForwardedHeaders?
啟用IIS集成
??3. 返回ConfigureWebHostDefaults代碼中的configure(webHostBuilder):執行Program類中的webBuilder.UseStartup<Startup>();
? ?以上過程完成了IHostBuilder.ConfigureWebHostDefaults,通過GenericWebHostBuilder對HostBuilder增加ASP.NET Core的運行時設置。
? ?接下來就是主機的Build過程了:
? 4.?CreateHostBuilder(args).Build()
??CreateHostBuilder返回的IHostBuilder,我們通過代碼Debug,看一下具體的類型:Microsoft.Extensions.Hosting.HostBuilder。
??
? ?具體的Build過程是怎么樣的?先看下Build的源碼:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/HostBuilder.cs
? ?? ?
? ? ? 主機Build的過程主要完成了:
BuildHostConfiguration:構造配置系統,初始化?IConfiguration?_hostConfiguration;
CreateHostingEnvironment:構建主機HostingEnvironment環境信息,包含ApplicationName、EnvironmentName、ContentRootPath等
CreateHostBuilderContext:創建主機Build上下文HostBuilderContext,上下文中包含:HostingEnvironment和Configuration
BuildAppConfiguration:構建應用程序配置
CreateServiceProvider:創建依賴注入服務提供程序,? 即依賴注入容器
四、主機是如何啟動運行的
? ?我們先通過Debug,看一下Host的信息:Microsoft.Extensions.Hosting.Internal.Host
? ?
? ? ??這個Run方法也是一個擴展方法:HostingAbstractionsHostExtensions.Run
? ? ? ASP.NET Core源代碼鏈接:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Abstractions/src/HostingAbstractionsHostExtensions.cs
? ? ?
? ???其實內部轉調的還是Host.StartAsync方法,在內部啟動了DI依賴注入容器中所有注冊的服務。
? ? ?.NET Core代碼鏈接:https://github.com/dotnet/extensions/blob/release/3.1/src/Hosting/Hosting/src/Internal/Host.cs
? ??
五、主機中注冊一個托管服務
? 以一個后臺自更新(每隔5s 檢查一次程序變更、進行輸出)場景作為Demo,我們看一下如何在主機中注冊一個托管服務。
? 自更新服務UpdateService,需要繼承接口IHostService。
??
public class UpdateService : IHostedService{Task updateTask = null;CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();public Task StartAsync(CancellationToken cancellationToken){updateTask = Task.Run(() =>{while (cancellationTokenSource.Token.IsCancellationRequested==false){<br> //Check new data...Console.WriteLine(DateTime.Now + ": Executed");Task.Delay(5000).Wait();}});return Task.CompletedTask;}public Task StopAsync(CancellationToken cancellationToken){cancellationTokenSource.Cancel();return Task.CompletedTask;}} public class UpdateService : IHostedService{Task updateTask = null;CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();public Task StartAsync(CancellationToken cancellationToken){updateTask = Task.Run(() =>{while (cancellationTokenSource.Token.IsCancellationRequested==false){<br> //Check new data...Console.WriteLine(DateTime.Now + ": Executed");Task.Delay(5000).Wait();}});return Task.CompletedTask;}public Task StopAsync(CancellationToken cancellationToken){cancellationTokenSource.Cancel();return Task.CompletedTask;}} 同時,我們需要在ConfigureServices方法中,將UpdateService添加到IoC服務容器中
程序啟動后,可以看到以下輸出:
? ??
? ?以上是對.NET Core主機的概念、設計初衷、構建過程、啟動運行過程、服務注冊的整理和分享。
往期精彩回顧
【.net core】電商平臺升級之微服務架構應用實戰
.Net Core微服務架構技術棧的那些事
Asp.Net Core 中IdentityServer4 授權中心之應用實戰
Asp.Net Core 中IdentityServer4 授權中心之自定義授權模式
Asp.Net Core 中IdentityServer4 授權流程及刷新Token
Asp.Net Core 中IdentityServer4 實戰之 Claim詳解
Asp.Net Core 中IdentityServer4 實戰之角色授權詳解
Asp.Net Core 中間件應用實戰中你不知道的那些事
Asp.Net Core Filter 深入淺出的那些事-AOP
Asp.Net Core EndPoint 終結點路由工作原理解讀
ASP.NET CORE 內置的IOC解讀及使用
??給個[在看],是對我最大的支持??
總結
以上是生活随笔為你收集整理的.NET Core技术研究-主机的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谁说.NET不适合搞大数据、机器学习和人
- 下一篇: ASP.NET Core 配置源:实时生