首頁 > 軟體

詳解ASP.NET Core高效能伺服器HTTP.SYS

2022-04-06 16:00:40

如果我們只需要將ASP.NET CORE應用部署到Windows環境下,並且希望獲得更好的效能,那麼我們選擇的伺服器型別應該是HTTP.SYS。Windows環境下任何針對HTTP的網路監聽器/伺服器在效能上都無法與HTTP.SYS比肩。

一、HTTP.SYS簡介

HTTP.SYS本質上就是一個HTTP/HTTPS監聽器,它是Windows網路子系統的一部分,是一個在核心模式下執行的網路驅動。HTTP.SYS對應的驅動檔案為“%WinDirSystem32drivershttp.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高效能伺服器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com