作者:cmliu;.NET Core启动都做了什么
.NET Core默认启动时的流程,您可以将图片另存为,在本地放大查看
.NET Core默认模板都做了些什么,首先贴出模板里面的Program.cs
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }
解读:
1,入口main方法首先调用CreateWebHostBuilder(string[] args)方法获取一个[Web主机构建者WebHostBuilder]
2,CreateWebHostBuilder(string[] args)方法首先是调用:WebHost.CreateDefaultBuilder(args)创建一个默认的WebHostBuilder
在WebHost.CreateDefaultBuilder中,首先读取了命令行,ContentRoot配置;然后指定Kestrel的配置,日志提供程序,以及相关优先执行的中间件,并明确注入了IServer的实现类为KestrelServer;
并配置了IIS相关的服//WebHost.CreateDefaultBuilder部分源码
public static IWebHostBuilder CreateDefaultBuilder(string[] args)
{ var builder = new WebHostBuilder(); if (string.IsNullOrEmpty(builder.GetSetting(WebHostDefaults.ContentRootKey))) { builder.UseContentRoot(Directory.GetCurrentDirectory()); } if (args != null) { builder.UseConfiguration(new ConfigurationBuilder().AddCommandLine(args).Build()); }
//该方法底层指定了Server的实现类为KestrelServer() builder.UseKestrel((builderContext, options) => { //省略其他代码
}) .ConfigureAppConfiguration((hostingContext, config) => { //省略其他代码 }) .ConfigureLogging((hostingContext, logging) => { //省略其他代码 }) .ConfigureServices((hostingContext, services) => { //省略其他代码 }) .UseIIS() .UseIISIntegration() .UseDefaultServiceProvider((context, options) => { //省略其他代码 }); return builder; }
3, 调用WebHost.CreateDefaultBuilder(args)返回对象的UseStartup<Startup>()方法;用于将服务注册到容器(ConfigureServices方法),以及注册管道(Configure方法)
这里也说明一下Startup中的ConfigureServices,Configure两个方法与【下面】的Program类中ConfigureServices,Configure方法作用是一样的,都是用于将服务注册到容器(ConfigureServices方法),以及注册管道(Configure方法)
public static IWebHostBuilder CreateWebHostBuilder(string[] args) { var webHostBuilder = WebHost.CreateDefaultBuilder(args).ConfigureServices((services) => { //省略其他代码,用于注册服务到容器 }).Configure((app) => { //省略其他代码,用于注册管道 }); return webHostBuilder; }
4,Statrtup中的代码,省略,后面进行解读
5,main方法中调用CreateWebHostBuilder(string[] args)返回的WebHostBuilder的Build方法
在这个Build方法中,直接初始化并返回了一个WebHost,并指定了WebHost的IServiceCollection(服务),IServiceProvider(容器),该WebHost有一个Server对象,对象实际上为KestrelServer
6,调用Build返回的WebHost对象的Run方法,用于启动服务器
*******实际上WebHost.Run方法中,首先获取一个ApplicationBuilder(就是Startip中的Configure方法中那个用于注入管道的类),并使用ApplicationBuilder.Build方法倒序构建RequestDelegate委托链管道
*******ApplicationBuilder.Build方法中,会把404中间件最为最后一个中间件注入到管道
*******使用获得的RequestDelegate,日志,HttpContextFactory相关参数,构造一个HostingApplication
*******最底层:以异步的方式启动WebHost.Server.StartAsync,即调用KestrelServer的StartAsync的方法,并在方法中指定了HostingApplication【此时KestrelServer已经有了一个拥有RequestDelegate管道的HostingApplication】
internal class WebHost : IWebHost { //省略其他代码 private readonly IServiceCollection _applicationServiceCollection; private readonly IServiceProvider _hostingServiceProvider; private IServiceProvider _applicationServices; private IServer Server { get; set; } //省略其他代码 public WebHost(IServiceCollection appServices,IServiceProvider hostingServiceProvider, WebHostOptions options,IConfiguration config,AggregateException hostingStartupErrors) { //省略其他代码 _applicationServiceCollection = appServices; _hostingServiceProvider = hostingServiceProvider; //省略其他代码 } //省略其他代码 public IServiceProvider Services { get { return _applicationServices; } } //省略其他代码 public void Start() { StartAsync().GetAwaiter().GetResult(); } //省略其他代码 public virtual async Task StartAsync(CancellationToken cancellationToken = default) { //省略其他代码 //构建RequestDelegate委托链管道 var application = BuildApplication(); _applicationLifetime = _applicationServices.GetRequiredService<IApplicationLifetime>() as ApplicationLifetime; _hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>(); var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticListener>(); var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>(); //RequestDelegate委托链管道用于构建HostingApplication var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory); //启动KestrelServer服务器,并将HostingApplication绑定到KestrelServer await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); //省略其他代码 } //省略其他代码 private RequestDelegate BuildApplication() { try { //省略其他代码 var builderFactory = _applicationServices.GetRequiredService<IApplicationBuilderFactory>(); var builder = builderFactory.CreateBuilder(Server.Features); builder.ApplicationServices = _applicationServices;
//省略其他代码
return builder.Build(); } catch (Exception ex) {
//省略其他代码
} } //省略其他代码 }
7,KestrelServer启动HttpListener后,开始监听来自客户端浏览器的请求
8,KestrelServer接收到浏览器请求后,将请求参数构造出HttpContext,并选择对应的HTTP协议处理类,通过协议处理类的ProcessRequests方法(传说中的PR方法)
public abstract partial class HttpProtocol : IHttpResponseControl { //HTTP协议类调用HttpApplication private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) { while (_keepAlive) { //省略其他代码 try { //省略其他代码 await application.ProcessRequestAsync(httpContext); //省略其他代码 } catch (BadHttpRequestException ex) { //省略其他代码 } //省略其他代码 } } }
9,PR方法会去调用KestrelServer的HostingApplication的ProcessRequestAsync,而ProcessRequestAsync则是把HttpContext交给RequestDelegate管道处理
public class HostingApplication : IHttpApplication<HostingApplication.Context> { //委托链管道 private readonly RequestDelegate _application; //省略其他代码 public HostingApplication( RequestDelegate application, ILogger logger, DiagnosticListener diagnosticSource, IHttpContextFactory httpContextFactory) { _application = application; //省略其他代码 } //PR方法,调用委托链处理请求 public Task ProcessRequestAsync(Context context) { return _application(context.HttpContext); }
//省略其他代码
}