ASP.NET Core程序如今變得如同控制檯(Console)程序通常,一樣經過Main方法啓動整個應用。而Main方法要作的事情很簡單,建立一個WebHostBuilder類,調用其Build方法生成一個WebHost類,最後啓動之。c++
實現代碼一目瞭然:json
public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }
要想探尋其內部究竟作了哪些操做,則須要調查下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()); } builder.UseKestrel((builderContext, options) => { options.Configure(builderContext.Configuration.GetSection("Kestrel")); }) .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()) { 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) => { logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); logging.AddConsole(); logging.AddDebug(); }) .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>(); }) .UseIISIntegration() .UseDefaultServiceProvider((context, options) => { options.ValidateScopes = context.HostingEnvironment.IsDevelopment(); }); return builder; }
代碼稍微有點多,但這裏只關心WebHostBuilder類的建立,以及該builder使用了UseKestrel方法。app
UseKestrel方法內部經過IoC的方式注入了KestrelServer類:異步
public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder) { return hostBuilder.ConfigureServices(services => { // Don't override an already-configured transport services.TryAddSingleton<ITransportFactory, SocketTransportFactory>(); services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>(); services.AddSingleton<IServer, KestrelServer>(); }); }
由此能夠知道當一個ASP.NET Core應用程序運行起來時,其內部會有KestrelServer。async
那麼爲何會須要這個KestrelServer?由於它能夠作爲一個反向代理服務器,幫助ASP.NET Core實現跨平臺的須要。ide
以傳統Windows系統上的IIS爲例,以下圖所示,ASP.NET Core應用程序中的代碼已經再也不直接依賴於IIS容器,而是經過KestrelServer這個代理將HTTP請求轉換爲HttpContext對象,再對此對象進行處理。函數
圖中的ASP.NET Core Module也是由ASP.NET Core的誕生而引入的新的IIS模塊。它的主要功能是將Web請求重定向至ASP.NET Core應用程序。而且因爲ASP.NET Core應用程序獨立運行於IIS工做進程以外的進程,它還負責對進程的管理。ui
ASP.NET Core Module的源碼由C++編寫,入口是main文件中的RegisterModule函數。this
其函數內部實例化了CProxyModuleFactory工廠類。
pFactory = new CProxyModuleFactory;
而由這個工廠類建立的CProxyModule實例中有一個關鍵的CProxyModule::OnExecuteRequestHandler方法。它會建立FORWARDING_HANDLER實例,並調用其OnExecuteRequestHandler方法。
__override REQUEST_NOTIFICATION_STATUS CProxyModule::OnExecuteRequestHandler( IHttpContext * pHttpContext, IHttpEventProvider * ) { m_pHandler = new FORWARDING_HANDLER(pHttpContext); if (m_pHandler == NULL) { pHttpContext->GetResponse()->SetStatus(500, "Internal Server Error", 0, E_OUTOFMEMORY); return RQ_NOTIFICATION_FINISH_REQUEST; } return m_pHandler->OnExecuteRequestHandler(); }
在此方法裏就有那些核心的處理HTTP請求的操做。
// 實例化應用程序管理器 pApplicationManager = APPLICATION_MANAGER::GetInstance(); // 取得應用程序實例 hr = pApplicationManager->GetApplication(m_pW3Context, &m_pApplication); // 取得該應用程序的進程 hr = m_pApplication->GetProcess(m_pW3Context, pAspNetCoreConfig, &pServerProcess); // 建立HTTP請求 hr = CreateWinHttpRequest(pRequest, pProtocol, hConnect, &struEscapedUrl, pAspNetCoreConfig, pServerProcess); // 發送HTTP請求 if (!WinHttpSendRequest(m_hRequest, m_pszHeaders, m_cchHeaders, NULL, 0, cbContentLength, reinterpret_cast<DWORD_PTR>(static_cast<PVOID>(this)))) { hr = HRESULT_FROM_WIN32(GetLastError()); DebugPrintf(ASPNETCORE_DEBUG_FLAG_INFO, "FORWARDING_HANDLER::OnExecuteRequestHandler, Send request failed"); goto Failure; }
在ASP.NET Core應用程序這端,CreateWebHostBuilder(args).Build().Run();
代碼執行以後,會調用其對應的異步方法:
private static async Task RunAsync(this IWebHost host, CancellationToken token, string shutdownMessage) { using (host) { await host.StartAsync(token); var hostingEnvironment = host.Services.GetService<IHostingEnvironment>(); var options = host.Services.GetRequiredService<WebHostOptions>(); if (!options.SuppressStatusMessages) { Console.WriteLine($"Hosting environment: {hostingEnvironment.EnvironmentName}"); Console.WriteLine($"Content root path: {hostingEnvironment.ContentRootPath}"); var serverAddresses = host.ServerFeatures.Get<IServerAddressesFeature>()?.Addresses; if (serverAddresses != null) { foreach (var address in serverAddresses) { Console.WriteLine($"Now listening on: {address}"); } } if (!string.IsNullOrEmpty(shutdownMessage)) { Console.WriteLine(shutdownMessage); } } await host.WaitForTokenShutdownAsync(token); } }
該方法中又調用了WebHost的StartAsync方法:
public virtual async Task StartAsync(CancellationToken cancellationToken = default) { HostingEventSource.Log.HostStart(); _logger = _applicationServices.GetRequiredService<ILogger<WebHost>>(); _logger.Starting(); var application = BuildApplication(); _applicationLifetime = _applicationServices.GetRequiredService<IApplicationLifetime>() as ApplicationLifetime; _hostedServiceExecutor = _applicationServices.GetRequiredService<HostedServiceExecutor>(); var diagnosticSource = _applicationServices.GetRequiredService<DiagnosticListener>(); var httpContextFactory = _applicationServices.GetRequiredService<IHttpContextFactory>(); var hostingApp = new HostingApplication(application, _logger, diagnosticSource, httpContextFactory); await Server.StartAsync(hostingApp, cancellationToken).ConfigureAwait(false); // Fire IApplicationLifetime.Started _applicationLifetime?.NotifyStarted(); // Fire IHostedService.Start await _hostedServiceExecutor.StartAsync(cancellationToken).ConfigureAwait(false); _logger.Started(); // Log the fact that we did load hosting startup assemblies. if (_logger.IsEnabled(LogLevel.Debug)) { foreach (var assembly in _options.GetFinalHostingStartupAssemblies()) { _logger.LogDebug("Loaded hosting startup assembly {assemblyName}", assembly); } } if (_hostingStartupErrors != null) { foreach (var exception in _hostingStartupErrors.InnerExceptions) { _logger.HostingStartupAssemblyError(exception); } } }
BuildApplication方法內部從IoC容器取出KestrelServer的實例:
private void EnsureServer() { if (Server == null) { Server = _applicationServices.GetRequiredService<IServer>(); var serverAddressesFeature = Server.Features?.Get<IServerAddressesFeature>(); var addresses = serverAddressesFeature?.Addresses; if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0) { var urls = _config[WebHostDefaults.ServerUrlsKey] ?? _config[DeprecatedServerUrlsKey]; if (!string.IsNullOrEmpty(urls)) { serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(_config, WebHostDefaults.PreferHostingUrlsKey); foreach (var value in urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { addresses.Add(value); } } } } }
最後調用KestrelServer的StartAsync方法:
public async Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) { try { if (!BitConverter.IsLittleEndian) { throw new PlatformNotSupportedException(CoreStrings.BigEndianNotSupported); } ValidateOptions(); if (_hasStarted) { // The server has already started and/or has not been cleaned up yet throw new InvalidOperationException(CoreStrings.ServerAlreadyStarted); } _hasStarted = true; _heartbeat.Start(); async Task OnBind(ListenOptions endpoint) { // Add the HTTP middleware as the terminal connection middleware endpoint.UseHttpServer(endpoint.ConnectionAdapters, ServiceContext, application, endpoint.Protocols); var connectionDelegate = endpoint.Build(); // Add the connection limit middleware if (Options.Limits.MaxConcurrentConnections.HasValue) { connectionDelegate = new ConnectionLimitMiddleware(connectionDelegate, Options.Limits.MaxConcurrentConnections.Value, Trace).OnConnectionAsync; } var connectionDispatcher = new ConnectionDispatcher(ServiceContext, connectionDelegate); var transport = _transportFactory.Create(endpoint, connectionDispatcher); _transports.Add(transport); await transport.BindAsync().ConfigureAwait(false); } await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false); } catch (Exception ex) { Trace.LogCritical(0, ex, "Unable to start Kestrel."); Dispose(); throw; } }
到了這一步,KestrelServer終於能夠監聽來自ASP.NET Core Module發出的HTTP請求,而ASP.NET Core應用程序也能夠開始其自身的任務處理了。