启动流程
- 创建并默认配置HostBuilder对象,用来构建Host的对象。
- HostBuilder对象配置参数,包括注册的服务,组件,请求处理管道相关内容(Startup 类 配置服务和应用的请求管道),增加了ASP.NET Core的运行时设置。
- 配置好的HostBuilder对象构建Host对象。
- Host对象启动.Run()。
- 控制台成为ASP.NET Core的web应用程序,开始监听请求。
- Host监听到请求后,请求需要经过Host配置的请求处理管道进行相关处理。
什么是Host
Host作为一个可以用来托管web程序的服务对象,用于封装应用资源以及应用程序启动和生存期管理,其主要功能包括配置初始化、创建托管环境和Host通用上下文、依赖注入等。封装应用资源有:
- 依赖注入框架 DI
- Logging日志
- Configuration 配置
- 托管服务:IHostedService服务接口的实现
WebHost即托管ASP.NET Core Web程序的服务对象。在低于 3.0 的 ASP.NET Core 版本中,WebHost用于 HTTP 工作负载。.NET Core提供WebHost的同时,还提供了一个通用Host的概念。通用Host和WebHost提供了类似的架构和功能,包含依赖注入框架DI、日志、配置、各类应用(托管服务)。通用Host的出现,给了我们更多开发的选择,比如说后台处理任务场景。在.NET Core3.1版本后,微软不再建议将 WebHost用于 Web 应用,直接使用通用Host来替换WebHost。
Host构建
从应用程序启动函数Main函数中看出构建Host的流程是首先调用接收命令行参数args的CreateHostBuilder方法,返回一个HostBuilder对象,然后HostBuilder对象.Build()方法完成构建Host对象。
Host.CreateDefaultBuilder(args)
Host.CreateDefaultBuilder方法内部操作对HostBuilder默认配置
- 将内容根目录(contentRootPath)设置为由 GetCurrentDirectory 返回的路径。
- 通过以下源加载主机配置
- 环境变量(DOTNET_前缀)配置
- 命令行参数配置
- 通过以下对象加载应用配置
- appsettings.json
- appsettings.{Environment}.json
- 密钥管理器 当应用在 Development 环境中运行时
- 环境变量
- 命令行参数
- 添加日志记录提供程序
- 控制台
- 调试
- EventSource
- EventLog( Windows环境下)
- 当环境为“开发”时,启用范围验证和依赖关系验证。
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
var builder = new HostBuilder();
builder.UseContentRoot(Directory.GetCurrentDirectory());//将内容根目录(contentRootPath)设置为由 GetCurrentDirectory 返回的路径。
builder.ConfigureHostConfiguration(config =>
{
config.AddEnvironmentVariables(prefix: "DOTNET_");//环境变量(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;
}
IHostBuilder.ConfigureWebHostDefaults
完成构造HostBuilder,然后代码继续调用了HostBuilder.ConfigureWebHostDefaults方法。通过GenericWebHostBuilder对HostBuilder增加ASP.NET Core的运行时设置。
IHostBuilder.ConfigureWebHostDefaults
public static class GenericHostBuilderExtensions
{
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure)
{
if (configure is null)
{
throw new ArgumentNullException(nameof(configure));
}
return builder.ConfigureWebHost(webHostBuilder =>
{
WebHost.ConfigureWebDefaults(webHostBuilder);
configure(webHostBuilder);
});
}
}
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;
}
}
GenericHostWebHostBuilderExtensions类中IHostBuilder的扩展方法ConfigureWebHost实现了对IWebHostBuilder的依赖注入,即将GenericWebHostBuilder实例传入方法ConfigureWebHostDefaults内部。通过GenericWebHostBuilder的构造函数GenericWebHostBuilder(buillder),将已有的HostBuilder增加了ASP.NET Core运行时设置。
总结IHostBuilder.ConfigureWebHostDefaults做了两件事情:
- 扩展IHostBuilder增加ConfigureWebHost,引入IWebHostBuilder的实现GenericWebHostBuilder,将已有的HostBuilder增加ASP.NET Core运行时的设置。
- ConfigureWebHost代码中的configure(webhostBuilder):对注入的IWebHostBuilder,调用 WebHost.ConfigureWebDefaults(webHostBuilder),启用各类设置。
WebHost.ConfigureWebDefaults(webHostBuilder)内部实现了:
- 前缀为 ASPNETCORE_ 的环境变量加载主机配置
- 将 Kestrel作为默认的Web服务器
- 添加HostFiltering中间件(主机筛选中间件)
- 如果ASPNETCORE_FORWARDEDHEADERS_ENABLED=true,添加转接头中间件ForwardedHeaders
- 启用IIS集成
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();
}
webBuilder.UseStartup();
返回IHostBuilder.ConfigureWebHostDefaults代码中的configure(webHostBuilder):执行Program类中的webBuilder.UseStartup();
CreateHostBuilder(args).Build()
- BuildHostConfiguration: 构造配置系统,初始化 IConfiguration _hostConfiguration;
- CreateHostingEnvironment:构建HostingEnvironment环境信息,包含ApplicationName、EnvironmentName、ContentRootPath等
- CreateHostBuilderContext:创建主机Build上下文HostBuilderContext,上下文中包含:HostingEnvironment和Configuration
- BuildAppConfiguration:构建应用程序配置
- CreateServiceProvider:创建依赖注入服务提供程序, 即依赖注入容器
public IHost Build()
{
if (_hostBuilt)
{
throw new InvalidOperationException("Build can only be called once.");
}
_hostBuilt = true;
BuildHostConfiguration();
CreateHostingEnvironment();
CreateHostBuilderContext();
BuildAppConfiguration();
CreateServiceProvider();
return _appServices.GetRequiredService<IHost>();
}
Host运行
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Extensions.Hosting
{
public static class HostingAbstractionsHostExtensions
{
/// <summary>
/// Starts the host synchronously.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to start.</param>
public static void Start(this IHost host)
{
host.StartAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Attempts to gracefully stop the host with the given timeout.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to stop.</param>
/// <param name="timeout">The timeout for stopping gracefully. Once expired the
/// server may terminate any remaining active connections.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public static Task StopAsync(this IHost host, TimeSpan timeout)
{
return host.StopAsync(new CancellationTokenSource(timeout).Token);
}
/// <summary>
/// Block the calling thread until shutdown is triggered via Ctrl+C or SIGTERM.
/// </summary>
/// <param name="host">The running <see cref="IHost"/>.</param>
public static void WaitForShutdown(this IHost host)
{
host.WaitForShutdownAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Runs an application and block the calling thread until host shutdown.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to run.</param>
public static void Run(this IHost host)
{
host.RunAsync().GetAwaiter().GetResult();
}
/// <summary>
/// Runs an application and returns a Task that only completes when the token is triggered or shutdown is triggered.
/// </summary>
/// <param name="host">The <see cref="IHost"/> to run.</param>
/// <param name="token">The token to trigger shutdown.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public static async Task RunAsync(this IHost host, CancellationToken token = default)
{
try
{
await host.StartAsync(token);
await host.WaitForShutdownAsync(token);
}
finally
{
if (host is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync();
}
else
{
host.Dispose();
}
}
}
/// <summary>
/// Returns a Task that completes when shutdown is triggered via the given token.
/// </summary>
/// <param name="host">The running <see cref="IHost"/>.</param>
/// <param name="token">The token to trigger shutdown.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public static async Task WaitForShutdownAsync(this IHost host, CancellationToken token = default)
{
var applicationLifetime = host.Services.GetService<IHostApplicationLifetime>();
token.Register(state =>
{
((IHostApplicationLifetime)state).StopApplication();
},
applicationLifetime);
var waitForStop = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
applicationLifetime.ApplicationStopping.Register(obj =>
{
var tcs = (TaskCompletionSource<object>)obj;
tcs.TrySetResult(null);
}, waitForStop);
await waitForStop.Task;
// Host will use its default ShutdownTimeout if none is specified.
await host.StopAsync();
}
}
}
IHost.Run内部调用了Host.StartAsync方法,在StartAsync方法内部启动了DI依赖注入容器中所有注册的服务。
参考:
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/host/web-host?view=aspnetcore-5.0
https://www.cnblogs.com/edison0621/p/11025310.html
https://www.cnblogs.com/tianqing/p/12726181.html