详解ASP.NET Core高性能服务器HTTP.SYS

 更新时间:2022年04月06日 12:02:48   作者:Artech  
HTTP.SYS本质上就是一个HTTP/HTTPS监听器,它是Windows网络子系统的一部分,是一个在内核模式下运行的网络驱动,今天通过本文给大家介绍下ASP.NET Core高性能服务器HTTP.SYS,感兴趣的朋友一起看看吧

如果我们只需要将ASP.NET CORE应用部署到Windows环境下,并且希望获得更好的性能,那么我们选择的服务器类型应该是HTTP.SYS。Windows环境下任何针对HTTP的网络监听器/服务器在性能上都无法与HTTP.SYS比肩。

一、HTTP.SYS简介

HTTP.SYS本质上就是一个HTTP/HTTPS监听器,它是Windows网络子系统的一部分,是一个在内核模式下运行的网络驱动。HTTP.SYS对应的驱动文件为“%WinDir\System32\drivers\http.sys”,不要小看这个只有1M多的文件,Windows系统针对HTTP的监听、接收、转发和响应大都依赖它。如图1所示,HTTP.SYS建立在Windows网络子系统针对TCPIP协议栈的驱动(TCPIP.SYS)之上,并为用户态运行的IIS提供基础的HTTP通信服务。前面我们使用的HttpListener也建立在HTTP.SYS上面。

图1 HTTP.SYS

由于HTTP.SYS是在操作系统内核态运行,所以它提供的性能优势是其他在用户态运行的同类产品无法比拟的。由于它自身提供响应缓存,所以在缓存命中的情况下根本不需要与用户态进程进行交互。它还提供了请求队列(Request Queue),如果请求的目标进程(比如IIS的工作进程)处于活动状态,它可以直接将请求分它给它,否则请求会暂存于队列中等待目标进程来提取,这样的工作模式既减少了内核态与用户态之间的上下文切换,也确保请求不会丢失。HTTP.SYS还提供连接管理,流量限制,诊断日志等功能,并提供针对Kerberos的Windows认证。

由于HTTP.SYS是一个底层共享的网络驱动,它有效地解决了端口共享的问题。用户态进程会使用地址前缀(含端口号)“接入”HTTP.SYS,后者利用提供的地址前缀来转发请求,多个用户态进程只要保证提供的地址前缀不同就可以了,所以它们可以使用相同的端口号。端口共享使每个用户进程都可以使用标准的80/443端口。

二、MessagePump & UseHttpSys

基于HTTP.SYS的服务器体现为如下这个MessagePump类型,它内部使用一个HttpSysListener对象采用注册的监听地址接入HTTP.SYS。MessagePump提供针对HTTP 1.X、HTTP 2以及HTTPS的支持。对于Windows Server 2022和Windows 11,还支持HTTP 3。IWebHostBuilder接口如下这两个UseHttpSys扩展方法用来完成针对MessagePump的注册。

internal class MessagePump : IServer, IDisposable
{
     internal HttpSysListener Listener { get; }
     public IFeatureCollection Features { get; }
     public MessagePump(IOptions<HttpSysOptions> options, ILoggerFactory loggerFactory,IAuthenticationSchemeProvider authentication);
     public Task StartAsync<TContext>(IHttpApplication<TContext> application,CancellationToken cancellationToken);
     public Task StopAsync(CancellationToken cancellationToken);
     public void Dispose();
}
public static class WebHostBuilderHttpSysExtensions
{
    [SupportedOSPlatform("windows")]
    public static IWebHostBuilder UseHttpSys(this IWebHostBuilder hostBuilder);

    [SupportedOSPlatform("windows")]
    public static IWebHostBuilder UseHttpSys(this IWebHostBuilder hostBuilder,Action<HttpSysOptions> options);
}

如下所示的是在Minimal API下调用UseHttpSys注册MessagePump 服务器的例子。

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys();
var app = builder.Build();
app.MapGet("/", () => "Hello World");
app.Run();

三、HttpSysOptions

在调用UseHttpSys扩展方法注册基于HTTP.SYS的MessagePump服务器的时候,我们可以利用提供的Action<HttpSysOptions>委托对相关的配置选项进行设置。HttpSysOptions的UrlPrefixes属性返回注册的监听地址前缀,但是最终是否这种直接注册到服务器上的监听器地址,取决于IServerAddressesFeature特性的PreferHostingUrls属性,这一点与KestrelServer是一致的。

public class HttpSysOptions
{
    public UrlPrefixCollection 	        UrlPrefixes { get; }
    public RequestQueueMode 		RequestQueueMode { get; set; }
    public string? 			RequestQueueName { get; set; }
    public long 			RequestQueueLimit { get; set; }
    public AuthenticationManager 	Authentication { get; }
    public ClientCertificateMethod 	ClientCertificateMethod { get; set; }
    public long? 			MaxConnections { get; set; }
    public long? 			MaxRequestBodySize { get; set; }
    public int 			        MaxAccepts { get; set; }
    public Http503VerbosityLevel 	Http503Verbosity { get; set; }
    public TimeoutManager 		Timeouts { get; }
    public bool 			AllowSynchronousIO { get; set; }
    public bool 			EnableResponseCaching { get; set; }
    public bool 			ThrowWriteExceptions { get; set; }
    public bool 			UnsafePreferInlineScheduling { get; set; }
    public bool 			UseLatin1RequestHeaders { get; set; }
}

HTTP.SYS利用请求队列来存放待处理的请求,我们可以利用RequestQueueMode属性决定创建一个新的队列或者使用现有的队列。该属性类型为如下这个RequestQueueMode枚举,枚举项Create表示创建新的队列,Attach表示使用现有的以RequestQueueName属性命名的对象,如果该队列不存在会抛出异常。CreateOrAttach提供了一个折中方案,如果指定名称的队列不存在就创建一个以此命名的新队列。该属性的默认值为Create,RequestQueueName属性默认值为Null(代表匿名队列),RequestQueueLimit属性表示队列的容量,默认值为1000。HttpSysOptions承载的很多配置选项只会应用到新创建的请求队列上。

public enum RequestQueueMode
{
    Create,
    Attach,
    CreateOrAttach
}

HttpSysOptions的Authentication属性返回一个AuthenticationManager对象,我们利用它完成针对认证的设置。我们可以利用Schemes属性设置认证方案,该属性默认为None。如果不允许匿名访问,可以将AllowAnonymous属性设为False。如果将AutomaticAuthentication属性返回True(默认值),认证用户将自动赋值给HttpContext上下文的User属性。AuthenticationDisplayName属性用来为认证方案提供一个显示名称。

public sealed class AuthenticationManager
{
    public AuthenticationSchemes 	Schemes { get; set; }
    public bool 			AllowAnonymous {get; set; }
    public bool 			AutomaticAuthentication { get; set; }
    public string? 			AuthenticationDisplayName { get; set; }
}

[Flags]
public enum AuthenticationSchemes
{
    None 				= 0x0,
    Digest 				= 0x1,
    Negotiate 				= 0x2,
    Ntlm 				= 0x4,
    Basic 				= 0x8,
    Anonymous 				= 0x8000,
    IntegratedWindowsAuthentication 	= 0x6
}

HTTPS站点可以要求提供证书来对其实施认证,HttpSysOptions的ClientCertificateMethod属性用于设置请求客户端证书的方式,该属性返回如下这个ClientCertificateMethod枚举。在.NET 5之前,客户端证书采用Renegotation的方式来提取的,Renegotiation是在已经建立的SSL/TLS连接上再次发起的一轮“协商握手”,这种方式对应AllowRenegotation枚举项。由于可能带来一些性能和死锁的问题,这种方式在.NET 5之后已经默认禁止了,目前默认的方式是创建SSL/TLS连接的初始阶段就提取该证书,这种方式对应AllowRenegotation枚举项,这也是ClientCertificateMethod属性的默认值。

public enum ClientCertificateMethod
{
    NoCertificate,
    AllowCertificate,
    AllowRenegotation
}

HttpSysOptions的MaxConnections和MaxRequestBodySize属性分别表示最大连接数和请求主体内容的最大字节数,如果它们被设置为Null,意味着忽略对应的限制。这两个属性的默认值分别Null和30,000,000。MaxAccepts属性表示接受的最大并发请求,默认值为当前处理器数量的5倍。如果并发请求数量超过限流设置,后续请求会拒绝处理,此时服务器会直接回复一个状态码为503的响应,与此同时还会根据Http503Verbosity属性设置的等级作相应的处理。如果该属性值为Basic(默认值),当前TCP连接会重置,Full和Limitmed选项会影响响应的状态描述,前者返回详细的Reason Phrase,后者采用标准的“Service Unavailable”。

public enum Http503VerbosityLevel
{
    Basic,
    Limited,
    Full
}

HttpSysOptions的Timeouts属性返回如下这个TimeoutManager对象,我们利用它完成各种超时设置,包括请求主体内容抵达时间(EntityBody)、读取请求主体内容时间(DrainEntityBody),请求在队列中存放的时间(RequestQueue)、连接闲置时间(IdleConnection)和解析请求报头时间(HeaderWait),这些超时时间默认都是两分钟。MinSendBytesPerSecond属性表示响应数据的最小发送率,默认为每秒150字节。

public sealed class TimeoutManager
{
    public TimeSpan 	EntityBody { get; set; }
    public TimeSpan 	DrainEntityBody { get; set; }
    public TimeSpan 	RequestQueue { get; set; }
    public TimeSpan 	IdleConnection { get; set; }
    public TimeSpan 	HeaderWait { get; set; }
    public long 	MinSendBytesPerSecond { get; set; }
}

HttpSysOptions还定义了其他一系列属性。AllowSynchronousIO属性(默认为False)表示是否运行以同步IO的方式完成针对请求和响应主体内容的读写。EnableResponseCaching属性(默认为True)表示允许响应缓存。ThrowWriteExceptions属性(默认为False)表示因断开连接导致写入响应主体内容失败是否需要抛出异常。如果将UnsafePreferInlineScheduling(默认为False)设置为True,意味着会直接在读取请求的IO线程中执行后续的应用代码,否则我们编写的应用代码会分发到线程池中进行处理。这样可以通过避免线程切换减少单个请求的处理耗时,但是会对整体的吞吐量带来负面影响。UseLatin1RequestHeaders属性(默认为False)表示是否采用Latin1字符集(ISO-8859-1)对请求报头进行编码。

到此这篇关于ASP.NET Core高性能服务器HTTP.SYS的文章就介绍到这了,更多相关ASP.NET Core高性能服务器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JWT+Log4net配置与使用详解

    JWT+Log4net配置与使用详解

    这篇文章主要介绍了JWT+Log4net配置与使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-07-07
  • .Net Core简单使用Mvc内置的Ioc(续)

    .Net Core简单使用Mvc内置的Ioc(续)

    怎样直接获取Ioc中的实例对象,而不是以构造函数的方式进行获取呢?这篇文章继续为大家介绍.Net Core简单使用Mvc内置的Ioc
    2018-03-03
  • ASP.NET 页面间数据传递方法小结

    ASP.NET 页面间数据传递方法小结

    Web 页面是无状态的, 服务器对每一次请求都认为来自不同用户,因此,变量的状态在连续对同一页面的多次请求之间或在页面跳转时不会被保留。
    2009-12-12
  • 用WPF实现屏幕文字提示的实现方法

    用WPF实现屏幕文字提示的实现方法

    本文介绍WPF应用程序实现在屏幕上显示一行或多行文字通知。它没有标题栏和最大化最小化等按钮,可以有半透明背景以使文字的显示更清晰,鼠标点击后提示消失。
    2013-07-07
  • Jwt通过源码揭秘隐藏大坑

    Jwt通过源码揭秘隐藏大坑

    今天分享一下在使用JWT在项目中遇到的一个问题,主要是一个协议的细节,非常容易被忽略,如果不是自己遇到,或者去看源码的实现,我估计至少80%的人都会栽在这里,下面来还原一下这个问题的过程,由于这个问题出现有一定的概率,不是每次都会出现,所以才容易掉坑里
    2022-05-05
  • ASP.NET使用正则表达式屏蔽垃圾信息

    ASP.NET使用正则表达式屏蔽垃圾信息

    找资料,看看如何实现使用正则表达式屏蔽垃圾信息,找来找去找出来的都不怎么好,有不详细的,有代码缺失的。最后还是找到了微软,先摘过来,大概思路已经有了,只需把下面微软给的样例代码修改一下即可,具体能不能行还不知道,先睡一觉,起来再慢慢研究。
    2008-09-09
  • ASP.net 页面被关闭后,服务器端是否仍然执行中?

    ASP.net 页面被关闭后,服务器端是否仍然执行中?

    当一个正在执行中的ASPX页面执行到一半的时候,浏览器中你关闭了这个页面,服务器端对应的这个页面的代码仍然在执行么?
    2008-08-08
  • 值类型和引用类型的区别深入理解

    值类型和引用类型的区别深入理解

    值类型通常被分配在栈上,它的变量直接包含变量的实例,使用效率比较高;引用类型分配在托管堆上,引用类型的变量通常包含一个指向实例的指针,变量通过该指针来引用实例,需要的朋友可以了解下
    2012-12-12
  • SignalR Self Host+MVC等多端消息推送服务(三)

    SignalR Self Host+MVC等多端消息推送服务(三)

    这篇文章主要为大家详细介绍了SignalR Self Host+MVC等多端消息推送服务的第三篇,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • 解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题

    这篇文章主要介绍了解决 .NET Core 中 GetHostAddressesAsync 引起的 EnyimMemcached 死锁问题的相关资料,需要的朋友可以参考下
    2016-09-09

最新评论