.NET Core开发日志——从ASP.NET Core Module到KestrelServer
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方法。
__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应用程序也可以开始其自身的任务处理了。
.NET Core开发日志——从ASP.NET Core Module到KestrelServer的更多相关文章
- C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志
C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...
- .NET Core开发日志——RequestDelegate
本文主要是对.NET Core开发日志--Middleware的补遗,但是会从看起来平平无奇的RequestDelegate开始叙述,所以以其作为标题,也是合情合理. RequestDelegate是 ...
- 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(1)
最近使用vscode比较多. 学习了一下如何在mac上使用vscode开发asp.netcore项目. 这里是我写的关于vscode的一篇文章: https://www.cnblogs.com/cgz ...
- .NET Core开发日志——Entity Framework与PostgreSQL
Entity Framework在.NET Core中被命名为Entity Framework Core.虽然一般会用于对SQL Server数据库进行数据操作,但其实它还支持其它数据库,这里就以Po ...
- 在CentOS7 开发与部署 asp.net core app笔记
原文:在CentOS7 开发与部署 asp.net core app笔记 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/lihongzhai/art ...
- .NET Core 2.0和ASP.NET Core 2.0正式版抢先体验
.NET Core 2.0和ASP.NET Core 2.0正式版抢先体验 .NET Standard 2.0 is final Broad platform support. .NET Standa ...
- List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac
List多个字段标识过滤 class Program{ public static void Main(string[] args) { List<T> list = new List& ...
- ASP.NET Core 基础教程总结 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 基础教程总结 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 基础教程总结 ASP.NET Core 基础教程总算是有了个简单 ...
- ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core Identity 迁移数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core Identity 迁移数据 上一章节中我们配置了 ...
随机推荐
- Android中windowTranslucentStatus与windowTranslucentNavigation的一些设置(转)
在iOS中,你可能发现页面会整体拉升到状态栏,整个页面效果就会显得更加的高端大气上档次,在Android4.4以后其实也有这种效果的实现,下面我就说一下在进行这种效果实现时碰到的一些坑,希望对大家有一 ...
- vbox磁盘空间如何扩容
vbox磁盘空间如何扩容 为虚拟机硬盘扩容(Oracle VM VirtualBox) VBoxManage modifyhd <uuid>|<filename& ...
- c++类大四个默认函数-构造函数 析构函数 拷贝构造函数 赋值构造函数
每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数).对于任意一个类A,如果不编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A ...
- angularjs入门初体验
1. http://www.zouyesheng.com/angular.html#toc39
- iOS的动态代理模式的实现
动态代理模式的应用很多,特别是在不能修改被代理类的前提下,要对执行某些方法时需要打log或者捕捉异常等处理时,是一个非常方便的方法.只需要少量修改客户端(场景类)代码和添加一个代理类就可以实现,这个符 ...
- THE OVERARCHING PROCESS OF TEST DESIGN
THE OVERARCHING PROCESS OF TEST DESIGN -Test note of “Essential Software Test Design” 2015-08-27 Con ...
- java解析Excel(xls、xlsx两种格式)
https://www.cnblogs.com/hhhshct/p/7255915.html ***************************************************** ...
- Javascript模版引擎简介
回顾 Micro-Templating 出自John Resig 2008年的一片文章,以及其经典实现: // Simple JavaScript Templating // John Resig - ...
- Android studio 怎么使用单元测试(不需要device)
关于单元测试的使用,写代码过程中有时候需要检测下代码逻辑的可行性与正确性,又不想通过设备运行,那么就可以通过单元测试跑下代码~ 1.首先建立一个Android studio的Android项目: 2. ...
- C语言学习之路
c语言学习 初识c语言 c语言数据类型.运算符和表达式(整数浮点数) 字符型数据/字符串 算术运算符和算术表达式(优先级,结合性等) 顺序程序设计(运算符之类内容,字符输入输出等) C/C++ 查看数 ...