ASP.NET Core程序现在变得如同控制台(Console)程序一般,同样通过Main方法启动整个应用。而Main方法要做的事情很简单,创建一个WebHostBuilder类,调用其Build方法生成一个WebHost类,最后启动之。
实现代码一目了然:
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方法。
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。
那么为什么会需要这个KestrelServer?因为它可以做为一个反向代理服务器,帮助ASP.NET Core实现跨平台的需要。
以传统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工作进程之外的进程,它还负责对进程的管理。
ASP.NET Core Module的源码由C++编写,入口是main文件中的RegisterModule函数。
其函数内部实例化了CProxyModuleFactory工厂类。
pFactory = new CProxyModuleFactory;
而由这个工厂类创建的CProxyModule实例中有一个关键的CProxyModule::OnExecuteRequestHandler方法。它会创建FORWARDING_HANDLER实例,并调用其OnExecuteRequestHandler方法。
__overrideREQUEST_NOTIFICATION_STATUSCProxyModule::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应用程序也可以开始其自身的任务处理了。
.NET Core开发日志——从ASP.NET Core Module到KestrelServer
原文地址:https://www.cnblogs.com/kenwoo/p/9309264.html