在【ASP.NET Core[源碼分析篇] - Startup】這篇文章中,咱們得知了目前爲止(UseStartup),全部的動做都是在_configureServicesDelegates裏面添加了註冊的委託,那麼系統是何時執行這些委託完成註冊的呢?html
經過以前的一系列眼花繚亂的操做,咱們獲得了全部須要註冊的委託_configureServicesDelegates,咱們看一下WebHostBuilder.Build如何實現真正的註冊。web
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實例中。app
須要注意的是,到此爲止程序並無執行Startup裏面的方法。async
當咱們的BuildCommonServices完成後,返回一個ServiceCollection實例,而且基於這個ServiceCollection實例生成了一個ServiceProvider對象,而後作爲生成WebHost對象的參數傳遞到WebHost中。ide
WebHost webHost = new WebHost(serviceCollection2, providerFromFactory, this._options, this._config, hostingStartupErrors); 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")); }
從上面的代碼流程能夠看出ui
至此,咱們的Startup類中的ConfigureServices已經執行過,而且WebHost已經具備了IServiceProvider對象 this
當咱們調用WebHost的擴展方法Run啓動應用的時候,本質上是調用了WebHost的StartAsync方法,這個過程建立了咱們應用程序最爲重要的用於監聽、接收、處理和響應HTTP請求的管道。 url
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啓動,咱們將分步進行剖析。
咱們先看一下這個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 是經過在UseUrls裏面指定的參數(WebHostDefaults.ServerUrlsKey) 或者是DeprecatedServerUrlsKey(配置文件裏面的server.urls)中來查找的。
在上面咱們獲取了一個Server用來監聽請求,那麼下一步咱們是要構建處理Http請求的管道,IApplicationBuilder 就是用於構建應用程序的請求管道。
咱們通常的管道建立是在 Startup 類的 Configure 方法中對 IApplicationBuilder 進行配置,嗯其實在這裏還有一個 IStartupFilter 也能夠用來配置 IApplicationBuilder,而且在 Startup 類的Configure 方法以前執行,全部咱們看到在BuildApplication方法中,一個大概的步驟是這樣的:
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; }
在這裏,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鏈接事件,剩下的就是開啓監聽了。
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的啓動監聽,涉及到了不少關鍵點,從代碼流程來看,只要抓住幾個關鍵點便可理解總體的一個流程。你們能夠帶着如下這些問題去跟着文章走: