_configureServicesDelegates 的承接
在 [ASP.NET Core[源码分析篇] - Startup] 这篇文章中, 我们得知了目前为止 (UseStartup), 所有的动作都是在_configureServicesDelegates 里面添加了注册的委托, 那么系统是什么时候执行这些委托完成注册的呢?
真正的注册
通过之前的一系列眼花缭乱的操作, 我们得到了所有需要注册的委托_configureServicesDelegates, 我们看一下 WebHostBuilder.Build 如何实现真正的注册.
- WebHostBuilder.Build()
- public IWebHost Build()
- {
- if (this._webHostBuilt)
- throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance);
- this._webHostBuilt = true;
- AggregateException hostingStartupErrors;
- IServiceCollection serviceCollection1 = this.BuildCommonServices(out hostingStartupErrors);
- IServiceCollection serviceCollection2 = serviceCollection1.Clone();
- IServiceProvider providerFromFactory = GetProviderFromFactory(serviceCollection1);
- .....
- WebHost webHost = new WebHost(serviceCollection2, providerFromFactory, this._options, this._config, hostingStartupErrors);
- try
- {
- webHost.Initialize();
- return (IWebHost) webHost;
- }
- catch
- {
- webHost.Dispose();
- throw;
- }
- IServiceProvider GetProviderFromFactory(IServiceCollection collection)
- {
- ServiceProvider serviceProvider = collection.BuildServiceProvider();
- IServiceProviderFactory<IServiceCollection> service = ((IServiceProvider) serviceProvider).GetService<IServiceProviderFactory<IServiceCollection>>();
- if (service == null)
- return (IServiceProvider) serviceProvider;
- using (serviceProvider)
- return service.CreateServiceProvider(service.CreateBuilder(collection));
- }
- }
这里面有个最重要的方法 BuildCommonServices, 这个方法实现了委托的真正的执行.
- private IServiceCollection BuildCommonServices(
- out AggregateException hostingStartupErrors)
- {
- .....
- ServiceCollection services = new ServiceCollection();
- services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>();
- services.AddTransient<IHttpContextFactory, HttpContextFactory>();
- services.AddScoped<IMiddlewareFactory, MiddlewareFactory>();
- services.AddOptions();
- services.AddLogging();
- services.AddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
- services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
- .....
- foreach (Action<WebHostBuilderContext, IServiceCollection> servicesDelegate in this._configureServicesDelegates)
- servicesDelegate(this._context, (IServiceCollection) services);
- return (IServiceCollection) services;
- }
从上面的代码我们可以看到, 首先创建了一个真正的 ServiceCollection 实例, 然后基于这个实例添加了一些额外的重要的注册 (ApplicationBuilderFactory,HttpContextFactory,DefaultServiceProviderFactory 等), 然后把这个 ServiceCollection 实例作为参数传递到_configureServicesDelegates 列表的各个委托中并执行, 这样的话所有在 Startup 需要注册的实例都已经注册在 services 这个 ServiceCollection 实例中.
需要注意的是, 到此为止程序并没有执行 Startup 里面的方法.
WebHost
当我们的 BuildCommonServices 完成后, 返回一个 ServiceCollection 实例, 并且基于这个 ServiceCollection 实例生成了一个 ServiceProvider 对象, 然后做为生成 WebHost 对象的参数传递到 WebHost 中.
- WebHost webHost = new WebHost(serviceCollection2, providerFromFactory, this._options, this._config, hostingStartupErrors);
- webHost.Initialize();
- WebHost.Initialize()
我们先看一下 WebHost 的 Initialize 方法
- public void Initialize()
- {
- try
- {
- this.EnsureApplicationServices();
- }
- catch (Exception ex)
- {
- if (this._applicationServices == null)
- this._applicationServices = (IServiceProvider) this._applicationServiceCollection.BuildServiceProvider();
- if (!this._options.CaptureStartupErrors)
- throw;
- else
- this._applicationServicesException = ExceptionDispatchInfo.Capture(ex);
- }
- }
- private void EnsureApplicationServices()
- {
- if (this._applicationServices != null)
- return;
- this.EnsureStartup();
- this._applicationServices = this._startup.ConfigureServices(this._applicationServiceCollection);
- }
- private void EnsureStartup()
- {
- if (this._startup != null)
- return;
- this._startup = this._hostingServiceProvider.GetService<IStartup>();
- if (this._startup == null)
- throw new InvalidOperationException(string.Format("No startup configured. Please specify startup via WebHostBuilder.UseStartup, WebHostBuilder.Configure, injecting {0} or specifying the startup assembly via {1} in the web host configuration.", (object) "IStartup", (object) "StartupAssemblyKey"));
- }
从上面的代码流程可以看出
解析 Startup 类
执行 Startup 类的 ConfigureServices 方法注册自定义的服务并返回一个 IServiceProvider 对象
直至, 我们的 Startup 类中的 ConfigureServices 已经执行过, 并且 WebHost 已经具有了 IServiceProvider 对象
WebHost.Run()
当我们调用 WebHost 的扩展方法 Run 启动应用的时候, 本质上是调用了 WebHost 的 StartAsync 方法, 这个过程创建了我们应用程序最为重要的用于监听, 接收, 处理和响应 HTTP 请求的管道.
- public virtual async Task StartAsync(CancellationToken cancellationToken = default (CancellationToken))
- {
- HostingEventSource.Log.HostStart();
- this._logger = this._applicationServices.GetRequiredService<ILogger<WebHost>>();
- this._logger.Starting();
- RequestDelegate application = this.BuildApplication();
- this._applicationLifetime = this._applicationServices.GetRequiredService<Microsoft.AspNetCore.Hosting.IApplicationLifetime>() as ApplicationLifetime;
- this._hostedServiceExecutor = this._applicationServices.GetRequiredService<HostedServiceExecutor>();
- DiagnosticListener requiredService1 = this._applicationServices.GetRequiredService<DiagnosticListener>();
- IHttpContextFactory requiredService2 = this._applicationServices.GetRequiredService<IHttpContextFactory>();
- ILogger<WebHost> logger = this._logger;
- DiagnosticListener diagnosticSource = requiredService1;
- IHttpContextFactory httpContextFactory = requiredService2;
- await this.Server.StartAsync<HostingApplication.Context>((IHttpApplication<HostingApplication.Context>) new HostingApplication(application, (ILogger) logger, diagnosticSource, httpContextFactory), cancellationToken).ConfigureAwait(false);
- this._applicationLifetime?.NotifyStarted();
- await this._hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false);
- .....
- }
- private RequestDelegate BuildApplication()
- {
- this._applicationServicesException?.Throw();
- this.EnsureServer();
- IApplicationBuilder builder = this._applicationServices.GetRequiredService<IApplicationBuilderFactory>().CreateBuilder(this.Server.Features);
- builder.ApplicationServices = this._applicationServices;
- IEnumerable<IStartupFilter> service = this._applicationServices.GetService<IEnumerable<IStartupFilter>>();
- Action<IApplicationBuilder> next = new Action<IApplicationBuilder>(this._startup.Configure);
- foreach (IStartupFilter startupFilter in service.Reverse<IStartupFilter>())
- next = startupFilter.Configure(next);
- next(builder);
- return builder.Build();
- }
- private void EnsureServer()
- {
- if (this.Server != null)
- return;
- this.Server = this._applicationServices.GetRequiredService<IServer>();
- IServerAddressesFeature addressesFeature = this.Server.Features?.Get<IServerAddressesFeature>();
- ICollection<string> addresses = addressesFeature?.Addresses;
- if (addresses == null || addresses.IsReadOnly || addresses.Count != 0)
- return;
- string str1 = this._config[WebHostDefaults.ServerUrlsKey] ?? this._config[WebHost.DeprecatedServerUrlsKey];
- if (string.IsNullOrEmpty(str1))
- return;
- addressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(this._config, WebHostDefaults.PreferHostingUrlsKey);
- string str2 = str1;
- char[] separator = new char[1]{ ';' };
- foreach (string str3 in str2.Split(separator, StringSplitOptions.RemoveEmptyEntries))
- addresses.Add(str3);
- }
这块主要是 Server 的创建, 管道的创建和监听 Http 请求的 Server 启动, 我们将分步进行剖析.
1. EnsureServer
我们先看一下这个 Server 是什么
- public interface IServer : IDisposable
- {
- IFeatureCollection Features { get; }
- Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken);
- Task StopAsync(CancellationToken cancellationToken);
- }
IServer 的实例其实是在开始 Program 里面的 CreateDefaultBuilder 中, 已经指定了 KestrelServer 作为默认的 Server 实例.
- public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder)
- {
- hostBuilder.UseLibuv();
- return hostBuilder.ConfigureServices(services =>
- {
- services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
- services.AddSingleton<IServer, KestrelServer>();
- });
- }
那么这个 Server 是做什么用的呢? Server 是一个 HTTP 服务器, 负责 HTTP 的监听, 接收一组 FeatureCollection 类型的原始请求, 并将其包装成 HttpContext 以供我们的应用程序完成响应的处理. 那它负责监听哪里? 从代码可以看到 Addresses 是通过在 launchSettings.JSON 或者是 ServerUrlsKey(UseUrls) 中来查找的.
2. BuildApplication
在上面我们获取了一个 Server 用来监听请求, 那么下一步我们是要构建处理 Http 请求的管道, IApplicationBuilder 就是用于构建应用程序的请求管道.
我们一般的管道创建是在 Startup 类的 Configure 方法中对 IApplicationBuilder 进行配置, 嗯其实在这里还有一个 IStartupFilter 也可以用来配置 IApplicationBuilder, 并且在 Startup 类的 Configure 方法之前执行, 所有我们看到在 BuildApplication 方法中, 一个大概的步骤是这样的:
基于 IApplicationBuilderFactory 创建 IApplicationBuilder 对象
基于 IStartupFilter 的管道构建
调用 IApplicationBuilder 对象的 Build 方法完成完整的管道
- public RequestDelegate Build()
- {
- RequestDelegate requestDelegate = (RequestDelegate) (context =>
- {
- context.Response.StatusCode = 404;
- return Task.CompletedTask;
- });
- foreach (Func<RequestDelegate, RequestDelegate> func in this._components.Reverse<Func<RequestDelegate, RequestDelegate>>())
- requestDelegate = func(requestDelegate);
- return requestDelegate;
- }
- 3. Server.StartAsync
在这里, Server 的启动是需要一个 IHttpApplication 类型的参数的, 来负责 HttpContext 的创建, 我们看一下这个参数
- public interface IHttpApplication<TContext>
- {
- TContext CreateContext(IFeatureCollection contextFeatures);
- Task ProcessRequestAsync(TContext context);
- void DisposeContext(TContext context, Exception exception);
- }
它的默认实现类是它的默认实现是 HostingApplication 类
- public class HostingApplication : IHttpApplication<HostingApplication.Context>
- {
- private readonly RequestDelegate _application;
- private readonly IHttpContextFactory _httpContextFactory;public Task ProcessRequestAsync(HostingApplication.Context context)
- {
- return this._application(context.HttpContext);
- }
- ......
- }
我们来看一下 Server 的 Http 监听绑定
- public async Task StartAsync<TContext>(
- IHttpApplication<TContext> application,
- CancellationToken cancellationToken)
- {
- try
- {
- if (!BitConverter.IsLittleEndian)
- throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported);
- this.ValidateOptions();
- if (this._hasStarted)
- throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted);
- this._hasStarted = true;
- this._heartbeat.Start();
- await AddressBinder.BindAsync(this._serverAddresses, this.Options, (ILogger) this.Trace, new Func<ListenOptions, Task>(OnBind)).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- this.Trace.LogCritical((EventId) 0, ex, "Unable to start Kestrel.");
- this.Dispose();
- throw;
- }
- async Task OnBind(ListenOptions endpoint)
- {
- endpoint.UseHttpServer<TContext>((IList<IConnectionAdapter>) endpoint.ConnectionAdapters, this.ServiceContext, application, endpoint.Protocols);
- ConnectionDelegate connectionDelegate = endpoint.Build();
- if (this.Options.Limits.MaxConcurrentConnections.HasValue)
- connectionDelegate = new ConnectionDelegate(new ConnectionLimitMiddleware(connectionDelegate, this.Options.Limits.MaxConcurrentConnections.Value, this.Trace).OnConnectionAsync);
- ConnectionDispatcher connectionDispatcher = new ConnectionDispatcher(this.ServiceContext, connectionDelegate);
- ITransport transport = this._transportFactory.Create((IEndPointInformation) endpoint, (IConnectionDispatcher) connectionDispatcher);
- this._transports.Add(transport);
- await transport.BindAsync().ConfigureAwait(false);
- }
- }
至此为止, Server 已经绑定一个监听端口, 注册了 HTTP 连接事件, 剩下的就是开启监听了.
4. HostedService
HostedService 为我们开启了一个后台运行服务, 它会在随着程序启动而启动.
- public class HostedServiceExecutor
- {
- private readonly IEnumerable<IHostedService> _services;public async Task StartAsync(CancellationToken token)
- {
- await this.ExecuteAsync((Func<IHostedService, Task>) (service => service.StartAsync(token)));
- }
- public async Task StopAsync(CancellationToken token)
- {
- await this.ExecuteAsync((Func<IHostedService, Task>) (service => service.StopAsync(token)));
- }
- private async Task ExecuteAsync(Func<IHostedService, Task> callback)
- {
- List<Exception> exceptions = (List<Exception>) null;
- foreach (IHostedService service in this._services)
- {
- try
- {
- await callback(service);
- }
- catch (Exception ex)
- {
- if (exceptions == null)
- exceptions = new List<Exception>();
- exceptions.Add(ex);
- }
- }
- if (exceptions != null)
- throw new AggregateException((IEnumerable<Exception>) exceptions);
- }
- }
总结
这两篇文章从 Startup 开始到最后的 Http 管道创建和 HttpServer 的启动监听, 涉及到了很多关键点, 从代码流程来看, 只要抓住几个关键点即可理解整体的一个流程. 大家可以带着以下这些问题去跟着文章走:
Startup 有多少种实例化方式?
IStartup 在哪里被实例化的?
IServiceCollection 何时实例化的?
IServiceProvider 何时实例化的?
Startup 的 ConfigureService 方法何时被执行?
IApplicationBuilder 何时实例化的?
Startup 的 Configure 方法何时被执行?
Http 监听管道是何时和如何构建的?
来源: https://www.cnblogs.com/lex-wu/p/11228612.html