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