乘风破浪,遇见最佳跨平台跨终端框架.Net Core/.Net生态 - 浅析ASP.NET Core全新跨平台内置Web服务器实现Kestrel(红隼)
什么是Kestrel
Kestrel(红隼)是一个跨平台的Web服务器实现。
Kestrel是包含在ASP.NET Core项目模板中的Web服务器,默认处于启用状态。
Kestrel支持以下方案:
- HTTPS
- HTTP/2(在macOS†上除外)
- 用于启用WebSocket的不透明升级
- 用于获得Nginx高性能的Unix套接字
macOS的未来版本将支持HTTP/2。
.NET Core支持的所有平台和版本均支持Kestrel。
Kestrel与HTTP.sys
Kestrel服务器是默认跨平台HTTP服务器实现。Kestrel提供了最佳性能和内存利用率,但它没有HTTP.sys中的某些高级功能。
与HTTP.sys相比,Kestrel具有以下优势:
- 更好的性能和内存利用率。
- 跨平台
- 灵活性,它是独立于操作系统进行开发和修补的。
- 编程端口和TLS配置
- 扩展性,允许PPv2等协议和备用传输。
Http.Sys作为共享内核模式组件运行,具有kestrel不具备的以下功能:
- 端口共享
- 内核模式Windows身份验证。Kestrel仅支持用户模式的身份验证。
- 通过队列传输的快速代理
- 直接文件传输
- 响应缓存
前生今世
2014年11月,ASP.NET 5(后来改名为ASP.NET Core)宣布成为跨平台技术。
显然,微软需要一种新的设计来支持Windows,macOS和Linux。而iis和window的强耦合基本上是无法移植到其他平台上的。
Kestrel是微软为了托管跨平台的ASP.NET Core定制的一款应用服务器容器。
.Net Hosting包括三种解决方案,Kestrel、HTTP.sys、IIS,其中IIS根据Hosting Models
还可以细分为进程内托管(In Prcoess)、进程外托管(Out Of Process)。
Kestrel
Kestrel可以作为独立的Process处理外部的HTTP Request,如下图
但因为Kestrel需要独占Port,在Multi-Tenant的部署环境,可以结合Reverse Proxy例如IIS、Nginx以及Apache来处理Http Request
HTTP.sys
HTTP.sys全称为HTTP.sys Web Server
,很容易和IIS扮演Listener同的HTTP.sys Driver
搞混。
HTTP.sys WebServer
是由HTTP.sys Driver
搭配HTTP Server API
所构成,可以替代Kestrel
使用,同样可以直接面对Http Request。
HTTP.sys
是Windows Server
平台限定,不同于Kestrel可以跨平台,而使用HTTP.sys的主要用途是处理Kestrel所无法处理的工作,例如Kernel Mode Windows Auth
。
但在一般情况下Microsoft是推荐优先使用Kestrel的噢
HTTP.sys Web Server的一个特色是无法搭配IIS使用,尽管两者同样是HTTP.sys Driver负责Listen Http Request。
IIS
进程外托管(Out Of Process)
Out Of Process Hosting Mode
相当于Kestrel使用Proxy的模式,对外是由IIS的w3wp.exe
来接待Http Request,再交由dotnet.exe
处理。
进程内托管(In Prcoess)
In Prcoess Hosting Mode
,系统只会有独立的Process,并且使用的是IIS Http Server
而非Kestrel,这种架构免去了Proxy代理的往返过程,效能上更为跃进,同时In Process也是预设将.NET部署至IIS所采用的模式。
源码迁移
最早Kestrel(紅隼)的代码是单独维护在https://github.com/aspnet/KestrelHttpServer,随着ASP.NET Core的发展,已经全面迁移到主库一起维护了:https://github.com/aspnet/AspNetCore
默认启用
未使用IIS托管时,ASP.NET Core项目模板默认使用Kestrel。在下面的模板生成的Program.cs
中,WebApplication.CreateBuilder
方法在内部调用UseKestrel
:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
//
// 摘要:
// Extension methods for configuring the IWebHostBuilder.
public static class GenericHostBuilderExtensions
{
//
// 摘要:
// Initializes a new instance of the Microsoft.AspNetCore.Hosting.IWebHostBuilder
// class with pre-configured defaults.
//
// 参数:
// builder:
// The Microsoft.Extensions.Hosting.IHostBuilder instance to configure
//
// configure:
// The configure callback
//
// 返回结果:
// The Microsoft.Extensions.Hosting.IHostBuilder for chaining.
//
// 言论:
// The following defaults are applied to the Microsoft.AspNetCore.Hosting.IWebHostBuilder:
// use Kestrel as the web server and configure it using the application's configuration
// providers, adds the HostFiltering middleware, adds the ForwardedHeaders middleware
// if ASPNETCORE_FORWARDEDHEADERS_ENABLED=true, and enable IIS integration.
public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure);
}
WebApplication.CreateBuilder的UseKestrel
internal static void ConfigureWebDefaults(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((ctx, cb) =>
{
if (ctx.HostingEnvironment.IsDevelopment())
{
StaticWebAssetsLoader.UseStaticWebAssets(ctx.HostingEnvironment, ctx.Configuration);
}
});
builder.UseKestrel((builderContext, options) =>
{
options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
})
.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>();
services.AddTransient<IStartupFilter, ForwardedHeadersStartupFilter>();
services.AddTransient<IConfigureOptions<ForwardedHeadersOptions>, ForwardedHeadersOptionsSetup>();
services.AddRouting();
})
.UseIIS()
.UseIISIntegration();
}
一般限制
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
});
webBuilder.UseStartup<Startup>();
});
{
"Kestrel": {
"Limits": {
"MaxConcurrentConnections": 100,
"MaxConcurrentUpgradedConnections": 100
},
"DisableStringReuse": true
}
}
保持活动状态超时
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 保持活动状态超时
kestrelServerOptions.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
});
webBuilder.UseStartup<Startup>();
});
客户端最大连接数
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 设置最大打开的连接数
kestrelServerOptions.Limits.MaxConcurrentConnections = 100;
// 设置最大打开、升级的连接数,升级的连接是已从HTTP切换到另一个协议(如WebSocket)的连接
kestrelServerOptions.Limits.MaxConcurrentUpgradedConnections = 100;
});
webBuilder.UseStartup<Startup>();
});
请求正文最大大小
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 设置允许的请求正文的最大大小(以字节为单位)
kestrelServerOptions.Limits.MaxRequestBodySize = 100_000_000;
});
webBuilder.UseStartup<Startup>();
});
请求正文最小数据速率
Kestrel每秒检查一次数据是否以指定的速率(字节/秒)传入。如果速率低于最小值,则连接超时。宽限期是Kestrel允许客户端将其发送速率提升到最小值的时间量。在此期间不会检查速率。宽限期有助于避免最初由于TCP慢启动而以较慢速率发送数据的连接中断。最小速率也适用于响应。
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 设置请求正文的最小数据速率(以字节/秒为单位)
kestrelServerOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
// 设置响应最小数据速率(以字节/秒为单位)
kestrelServerOptions.Limits.MinResponseDataRate = new MinDataRate(
bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
});
webBuilder.UseStartup<Startup>();
});
请求标头超时
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 设置服务器接收请求头所需的最大时间量
kestrelServerOptions.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(1);
});
webBuilder.UseStartup<Startup>();
});
HTTP/2限制
每个连接的最大流
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 限制每个HTTP/2 连接的并发请求流的数量(拒绝过多的流)
kestrelServerOptions.Limits.Http2.MaxStreamsPerConnection = 100;
});
webBuilder.UseStartup<Startup>();
});
标题表大小
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 限制了服务器上HPACK编码器与解码器可以使用的标头压缩表的大小(以八进制数表示)
kestrelServerOptions.Limits.Http2.HeaderTableSize = 4096;
});
webBuilder.UseStartup<Startup>();
});
最大帧大小
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 指示允许接收的最大帧有效负载的大小(以八进制数表示)
kestrelServerOptions.Limits.Http2.MaxFrameSize = 16_384;
});
webBuilder.UseStartup<Startup>();
});
最大请求标头大小
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 指示请求头字段序列的最大允许大小
kestrelServerOptions.Limits.Http2.MaxRequestHeaderFieldSize = 8192;
});
webBuilder.UseStartup<Startup>();
});
初始连接窗口大小
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 服务器一次愿意接收和缓冲多少请求正文数据
kestrelServerOptions.Limits.Http2.InitialConnectionWindowSize = 131_072;
});
webBuilder.UseStartup<Startup>();
});
初始流窗口大小
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 服务器愿意为每个流一次接收和缓冲多少请求正文数据
kestrelServerOptions.Limits.Http2.InitialStreamWindowSize = 98_304;
});
webBuilder.UseStartup<Startup>();
});
保持活动ping配置
Kestrel可以配置为向连接的客户端发送HTTP/2 Ping。
HTTP/2 Ping有多种用途:
- 使空闲连接保持活动状态。某些客户端和代理服务器会关闭空闲的连接。HTTP/2 Ping是对连接执行的活动,可防止空闲连接被关闭。
- 关闭不正常的连接。服务器会关闭在配置的时间内客户端未响应保持活动ping的连接。
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 配置ping间隔的TimeSpan。如果服务器在此时间段内没有收到任何帧,则服务器会向客户端发送保持活动ping。将此选项设置为TimeSpan.MaxValue时,会禁用保持活动ping。
kestrelServerOptions.Limits.Http2.KeepAlivePingDelay = TimeSpan.FromSeconds(30);
// 配置ping超时的TimeSpan。如果服务器在此超时期间没有收到任何帧(如响应ping),则连接将关闭。将此选项设置为TimeSpan.MaxValue时,会禁用保持活动状态超时。
kestrelServerOptions.Limits.Http2.KeepAlivePingTimeout = TimeSpan.FromMinutes(1);
});
webBuilder.UseStartup<Startup>();
});
同步I/O
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions =>
{
// 配置Kestrel
// 控制是否允许对请求和响应使用同步I/O
kestrelServerOptions.AllowSynchronousIO = true;
});
webBuilder.UseStartup<Startup>();
});
大量阻止同步I/O的操作可能会导致线程池资源不足,进而导致应用无响应。仅在使用不支持异步I/O的库时,才启用AllowSynchronousIO
参考
- ASP.NET Core中的Kestrel Web服务器实现
- 深入理解kestrel的应用
- Kestrel服务器
- 为ASP.NET Core Kestrel Web服务器配置选项
- 使用Kestrel为你的ASP.NET Core服务添加https支持
- ASP.NET Core中的HTTP.sys Web服务器实现
- 使用IIS和ASP.NET Core进行进程外托管
- 使用IIS和ASP.NET Core进行进程内托管
- ASP.NET Core In Process Hosting on IIS with ASP.NET Core
- .NET Hosting Solutions (Kestrel, HTTP.sys, IIS)
- kestral (红隼)
- ASP.NET Core中的Web服务器实现
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步