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


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

    一、HTTP.SYS简介
    二、MessagePump & UseHttpSys
    三、HttpSysOptions

    一、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上面。

    image
    图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)对请求报头进行编码。

  • 相关阅读:
    R函数optim()最小化或者最大化多参数函数
    C++11 Thead线程库的基本使用
    最新咸鱼签名JS逆向分析教程
    2022年GPS广播星历精密星历如何下载
    反射、枚举以及lambda表达式
    行测--常识扩展
    Docker 安装使用操作指南
    Eureka Series : Thinking in Dissertations
    实至名归!优维科技荣膺NIISA联盟2022年度双项技术创新奖
    网络安全(黑客)—高效自学
  • 原文地址:https://www.cnblogs.com/artech/p/inside-asp-net-core-6-33.html