KestrelServer詳解[1]:註冊監聽終結點(Endpoint)
具有跨平臺能力的KestrelServer是最重要的伺服器型別。針對KestrelServer的設定均體現在KestrelServerOptions配置選項上,註冊的終結點是它承載的最重要的配置選項。這裡所謂的終結點(Endpoint)與“路由”介紹的終結點不是一回事,這裡表示的就是伺服器在監聽請求時繫結的網路地址,對應著一個System.Net.Endpoint物件。我們知道ASP.NET Core應用承載API也提供了註冊監聽地址的方法,其本質其實也是為了註冊終結點,那麼兩種註冊方式如何取捨呢?本文提供的示例演示已經同步到《ASP.NET Core 6框架揭祕-例項演示版》)
一、UseKestrel擴充套件方法
二、兩種終結點的取捨
三、終結點配置
四、針對HTTPS的設定
五、限制約束
六、其他設定
一、UseKestrel擴充套件方法
IWebHostBuilder介面如下三個UseKestrel擴充套件方法過載會幫助我們完成KestrelServer的註冊並對KestrelServerOptions配置選項作相應設定,我們先來看看如何利用它們來註冊終結點。
public static class WebHostBuilderKestrelExtensions { public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder); public static IWebHostBuilder UseKestrel(thisIWebHostBuilder hostBuilder,Action<KestrelServerOptions> options); public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, KestrelServerOptions> configureOptions); }
註冊到KestrelServer上的終結點體現為如下這個Endpoint物件。Endpoint是對網路地址的抽象,它們在大部分下體現為“IP地址+埠
public abstract class EndPoint { public virtual AddressFamily AddressFamily { get; } public virtual EndPoint Create(SocketAddress socketAddress); public virtual SocketAddress Serialize(); } public class IPEndPoint : EndPoint public class DnsEndPoint : EndPoint public sealed class UnixDomainSocketEndPoint : EndPoint public class FileHandleEndPoint : EndPoint
終結點註冊利用如下這個ListenOptions配置選項來描述。該型別實現的IConnectionBuilder和IMultiplexedConnectionBuilder介面涉及針對連線的構建,我們將在後面討論這個話題。註冊的終結點體現為該配置選項的EndPoint屬性,如果是一個IPEndPoint物件,該物件也會體現在IPEndPoint屬性上。如果終結點型別為UnixDomainSocketEndPoint和FileHandleEndPoint,我們可以利用配置選項的SocketPath和FileHandle得到對應的Socket路徑和檔案控制代碼。
public class ListenOptions : IConnectionBuilder, IMultiplexedConnectionBuilder { public EndPoint EndPoint { get; } public IPEndPoint IPEndPoint { get; } public string SocketPath { get; } public ulong FileHandle { get; } public HttpProtocols Protocols { get; set; } public bool DisableAltSvcHeader { get; set; } public IServiceProvider ApplicationServices { get; } public KestrelServerOptions KestrelServerOptions { get; } ... }
同一個終結點可以同時支援HTTP 1.x、HTTP 2 和HTTP 3三種協議,具體設定體現在Protocols屬性上,該屬性返回如下這個HttpProtocols列舉。由於列舉項Http3和Http1AndHttp2AndHttp3上面標註了RequiresPreviewFeaturesAttribute特性,如果需要採用HTTP 3協議,專案檔案中必須新增“<EnablePreviewFeatures>true</EnablePreviewFeatures>”屬性。如果HTTP3終結點同時支援HTTP 1.X和HTTP 2,針對HTTP 1.X和HTTP 2的請求的響應一般會新增一個alt-svc (Alternative Service)報頭指示可以升級到HTTP 3,我們可以設定DisableAltSvcHeader屬性關閉此特性。該屬性預設值為Http1AndHttp2。
[Flags] public enum HttpProtocols { None = 0, Http1 = 1, Http2 = 2, Http1AndHttp2 = 3, [RequiresPreviewFeatures] Http3 = 4, [RequiresPreviewFeatures] Http1AndHttp2AndHttp3 = 7 }
KestrelServerOptions的ListenOptions屬性返回的ListenOptions列表代表所有註冊的終結點,它由CodeBackedListenOptions和ConfigurationBackedListenOptions屬性合併而成,這兩個屬性分別表示通過程式碼和配置註冊的終結點。基於“程式碼”的終結點註冊由如下所示的一系列Listen和以“Listen”為字首的方法來完成。除了這些註冊單個終結點的方法, ConfigureEndpointDefaults方法為註冊的所有終結點提供基礎設定。
public class KestrelServerOptions { internal List<ListenOptions> CodeBackedListenOptions { get; } internal List<ListenOptions> ConfigurationBackedListenOptions { get; } internal IEnumerable<ListenOptions> ListenOptions { get; } public void Listen(EndPoint endPoint); public void Listen(IPEndPoint endPoint); public void Listen(EndPoint endPoint, Action<ListenOptions> configure); public void Listen(IPAddress address, int port); public void Listen(IPEndPoint endPoint, Action<ListenOptions> configure); public void Listen(IPAddress address, int port, Action<ListenOptions> configure); public void ListenAnyIP(int port); public void ListenAnyIP(int port, Action<ListenOptions> configure); public void ListenHandle(ulong handle); public void ListenHandle(ulong handle, Action<ListenOptions> configure); public void ListenLocalhost(int port); public void ListenLocalhost(int port, Action<ListenOptions> configure); public void ListenUnixSocket(string socketPath); public void ListenUnixSocket(string socketPath, Action<ListenOptions> configure); public void ConfigureEndpointDefaults(Action<ListenOptions> configureOptions) ... }
二、兩種終結點的取捨
我們知道監聽地址不僅可以新增到WebApplication物件的Urls屬性中,WebApplication型別用來啟動應用的RunAsync和Run方法也提供了可預設的引數url來指定監聽地址。從如下的程式碼片段可以看出,這三種方式提供的監聽地址都被新增到了IServerAddressesFeature特性的Addresses屬性中。
public sealed class WebApplication : IHost { private readonly IHost _host; public ICollection<string> Urls => _host.Services.GetRequiredService<IServer>().Features .Get<IServerAddressesFeature>()?.Addresses
?? throw new InvalidOperationException("IServerAddressesFeature could not be found."); public Task RunAsync(string? url = null) { Listen(url); return ((IHost)this).RunAsync(); } public void Run(string? url = null) { Listen(url); ((IHost)this).Run(); } private void Listen(string? url) { if (url != null) { var addresses = ServerFeatures.Get<IServerAddressesFeature>()?.Addresses?? throw new InvalidOperationException("No valid IServerAddressesFeature is found"); addresses.Clear(); addresses.Add(url); } } }
如果KestrelServerOptions配置選項不能提供註冊的終結點,那麼KestrelServer就會使用IServerAddressesFeature特性提供的地址來建立對應的終結點,否則就會根據它的PreferHostingUrls屬性來進行取捨。如果IServerAddressesFeature特性的PreferHostingUrls屬性返回True,它提供的地址會被選擇,否則就使用直接註冊到KestrelServerOptions配置選項的終結點。針對監聽地址的註冊和PreferHostingUrls的設定可以利用IWebHostBuilder介面如下兩個擴充套件方法來完成。從給出的程式碼片段可以看出這兩個方法會將提供的設定儲存配置上,配置項名稱分別為“urls”和“preferHostingUrls”,對應著WebHostDefaults定義的兩個靜態只讀欄位ServerUrlsKey和PreferHostingUrlsKey。既然這兩個設定來源於配置,我們自然可以利用命令列引數、環境變數或者直接修改對應配置項的方式來指定它們。
public static class HostingAbstractionsWebHostBuilderExtensions { public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls) => hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(';', urls)); public static IWebHostBuilder PreferHostingUrls(this IWebHostBuilder hostBuilder, bool preferHostingUrls) => hostBuilder.UseSetting(WebHostDefaults.PreferHostingUrlsKey, preferHostingUrls ? "true" : "false"); }
如果伺服器的特性集合提供的IServerAddressesFeature特性包含監聽地址,以配置方式設定的監聽地址和針對PreferHostingUrls的設定將會被忽略,這一個特性體現在GenericWebHostService的StartAsync方法中。如下面的程式碼片段所示,該方法會從伺服器中提取IServerAddressesFeature特性,只有該特性不能提供監聽地址的情況下,利用配置註冊的監聽地址和針對PreferHostingUrls的設定才會應用到該特性中。
internal sealed class GenericWebHostService : IHostedService { public async Task StartAsync(CancellationToken cancellationToken) { ... var serverAddressesFeature = Server.Features.Get<IServerAddressesFeature>(); var addresses = serverAddressesFeature?.Addresses; if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0) { var text = Configuration[WebHostDefaults.ServerUrlsKey]; if (!string.IsNullOrEmpty(text)) { serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey); string[] array = text.Split(';', StringSplitOptions.RemoveEmptyEntries); foreach (string item in array) { addresses.Add(item); } } } } }
下面的演示程式通過呼叫IWebHostBuilder介面的UseKestrel擴充套件方法註冊了一個採用8000埠的本地終結點,通過呼叫UseUrls擴充套件方法註冊了一個採用9000埠的監聽地址。
var builder = WebApplication.CreateBuilder(args);
builder.WebHost
.UseKestrel(kestrel => kestrel.ListenLocalhost(8000))
.UseUrls("http://localhost:9000");
var app = builder.Build();
app.Run();
我們以命令列的方式兩次啟動了該程式。預設情況下應用會選擇呼叫UseKestrel擴充套件方法註冊的終結點。如果指定了命令列引數“preferHostingUrls=1”,那麼最終使用的都是將是呼叫UseUrls擴充套件方法註冊的監聽地址。由於兩種情況都涉及到放棄某種設定,所以輸出了相應的日誌。
三、終結點配置
KestrelServerOptions承載的很多設定都可以利用配置來提供。由於該配置選項型別的定義與配置的結構存在差異, KestrelServerOptions配置選項無法直接使用對應的IConfiguration物件進行繫結,所以KestrelServerOptions型別定義如下三個Configure方法。後面兩個方法提供了承載配置內容的IConfiguration物件,最後一個過載還提供了reloadOnChange引數來決定是否自動載入更新後的配置。第一個過載提供的其實是一個空的IConfiguration物件。
public class KestrelServerOptions { public KestrelConfigurationLoader Configure(); public KestrelConfigurationLoader Configure(IConfiguration config); public KestrelConfigurationLoader Configure(IConfiguration config, bool reloadOnChange) }
三個Configure方法都返回KestrelConfigurationLoader物件,後者是對當前KestrelServerOptions配置選項和指定IConfiguration物件的封裝。KestrelConfigurationLoader的Load方法會讀取配置的內容並將其應用到KestrelServerOptions配置選項上,該型別還提供了一系列註冊各類終結點的方法。
public class KestrelConfigurationLoader { public KestrelServerOptions Options { get; } public IConfiguration Configuration { get; } public KestrelConfigurationLoader Endpoint(string name, Action<EndpointConfiguration> configureOptions); public KestrelConfigurationLoader Endpoint(IPAddress address, int port); public KestrelConfigurationLoader Endpoint(IPAddress address, int port, Action<ListenOptions> configure); public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint); public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint, Action<ListenOptions> configure); public KestrelConfigurationLoader LocalhostEndpoint(int port); public KestrelConfigurationLoader LocalhostEndpoint(int port, Action<ListenOptions> configure); public KestrelConfigurationLoader AnyIPEndpoint(int port); public KestrelConfigurationLoader AnyIPEndpoint(int port, Action<ListenOptions> configure); public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath); public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath, Action<ListenOptions> configure); public KestrelConfigurationLoader HandleEndpoint(ulong handle); public KestrelConfigurationLoader HandleEndpoint(ulong handle, Action<ListenOptions> configure); public void Load(); }
ASP.NET Core應用在啟動時會呼叫IHostBuilder介面如下這個ConfigureWebHostDefaults擴充套件方法進行初始化設定,該方法會從當前配置中提取出“Kestrel”配置節,並將其作為引數呼叫Configure方法將配置內容應用到KestrelServerOptions配置選項上。由於reloadOnChange引數被設定成了True,所以更新後的配置會自動被重新載入。
public static class GenericHostBuilderExtensions { public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure) => builder.ConfigureWebHost(webHostBuilder => { WebHost.ConfigureWebDefaults(webHostBuilder); configure(webHostBuilder); }); } public static class WebHost { internal static void ConfigureWebDefaults(IWebHostBuilder builder) { ... builder.UseKestrel((builderContext, options) => { options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true); }) ... } }
如下的程式碼片段展現了針對終結點的配置。我們在“Kestrel:Endpoints”配置了兩個分別命名為“endpoint1”和“endpoint2”終結點,它們採用的監聽地址分別為“http://localhost:9000”和“https://localhost:9001”。KestrelServerOptions絕大部分配置選項都可以定義在配置檔案中,具體的配置定義方法可以參閱官方文件。
{ "Kestrel": { "Endpoints": { "endpoint1": { "Url": "http://localhost:9000" }, "endpoint2": { "Url": "https://localhost:9001" } } } }
四、針對HTTPS的設定
較之普通的終結點,HTTPS(SSL/TLS)終結點需要提供額外的設定,這些設定大都體現在如下這個HttpsConnectionAdapterOptions配置選項上。KestrelServerOptions的ConfigureHttpsDefaults方法為所有HTTPS終結點提供了預設的設定。
public class HttpsConnectionAdapterOptions { public X509Certificate2? ServerCertificate { get; set; } public Func<ConnectionContext?, string?, X509Certificate2?>? ServerCertificateSelector { get; set; } public TimeSpan HandshakeTimeout { get; set; } public SslProtocols SslProtocols { get; set; } public Action<ConnectionContext, SslServerAuthenticationOptions>? OnAuthenticate { get; set; } public ClientCertificateMode ClientCertificateMode { get; set; } public Func<X509Certificate2, X509Chain?, SslPolicyErrors, bool>? ClientCertificateValidation { get; set; } public bool CheckCertificateRevocation { get; set; } public void AllowAnyClientCertificate() { get; set; } } public static class KestrelServerOptions { public void ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions> configureOptions); ... }
表示服務端證書的X509Certificate2物件可以直接設定到ServerCertificate屬性上,我們也可以在ServerCertificateSelector屬性上設定一個根據當前連結動態選擇證書的委託。SslProtocols屬性用來設定採用的協議(SSL或者TLS),對應的型別為如下這個SslProtocols列舉。HandshakeTimeout屬性用來設定TLS/SSL“握手”的超時時間,預設為10秒。
[Flags] public enum SslProtocols { None = 0x0, [Obsolete("SslProtocols.Ssl2 has been deprecated and is not supported.")] Ssl2 = 0xC, [Obsolete("SslProtocols.Ssl3 has been deprecated and is not supported.")] Ssl3 = 0x30, Tls = 0xC0, [Obsolete("SslProtocols.Default has been deprecated and is not supported.")] Default = 0xF0, Tls11 = 0x300, Tls12 = 0xC00, Tls13 = 0x3000 }
HTTPS主要解決的是服務端的認證和傳輸安全問題,所以服務端的認證資訊需要在前期“協商”階段利用建立的安全通道傳遞給客戶端,具體的認證資訊是如下這個SslServerAuthenticationOptions配置選項格式化後的結果。HttpsConnectionAdapterOptions的OnAuthenticate屬性提供的委託可以幫助我們對這個配置選項進行設定,所以絕大部分HTTPS相關的設定都可以利用該屬性來完成。
public class SslServerAuthenticationOptions { public bool AllowRenegotiation { get; set; } public bool ClientCertificateRequired { get; set; } public List<SslApplicationProtocol>? ApplicationProtocols { get; set; } public RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; } public ServerCertificateSelectionCallback? ServerCertificateSelectionCallback { get; set; } public X509Certificate? ServerCertificate { get; set; } public SslStreamCertificateContext? ServerCertificateContext { get; set; } public SslProtocols EnabledSslProtocols { get; set; } public X509RevocationMode CertificateRevocationCheckMode { get; set; } public EncryptionPolicy EncryptionPolicy { get; set; } public CipherSuitesPolicy? CipherSuitesPolicy { get; set; } }
HTTPS不僅僅能夠幫助客戶端來驗證服務端的身份,還能幫助服務端來對客戶端身份進行驗證。服務端驗證利用服務端證書來完成,與之類似,服務端要識別客戶端的身份,同樣需要客戶端提供證書。我們可以利用HttpsConnectionAdapterOptions的ClientCertificateMode屬性來決定是否要求客戶端提供證書,該屬性型別為如下這個ClientCertificateMode列舉。針對客戶端認證的驗證可以利用ClientCertificateValidation屬性設定的委託來完成。
public enum ClientCertificateMode { NoCertificate, AllowCertificate, RequireCertificate, DelayCertificate }
由權威機構(Certificate Authority)頒發的證書可能會由於某種原因被撤銷,我們有兩種途徑來確定某張證書是否處於被撤銷的狀態:證書頒發機構可以採用標準的OCSP(Online Certificate Status Protocol)協議提供用於確定證書狀態的API,也可以直接提供一份撤銷的證書清單(CRL:Certificate Revocation List)。HttpsConnectionAdapterOptions的CheckCertificateRevocation屬性用來決定是否需要對證書的撤銷狀態進行驗證。如果不需要對客戶端證書作任何驗證,我們可以呼叫HttpsConnectionAdapterOptions的AllowAnyClientCertificate方法。
當我們將某個終結點註冊到KestrelServer上並生成對應ListenOptions配置選項後,我們可以呼叫後者的UseHttps擴充套件方法(註冊終結點的很多方法都提供一個Action<ListenOptions>引數)完成針對HTTPS的設定,我們有如下這一系列UseHttps過載可供選擇。對於證書的設定,我們可以直接指定一個X509Certificate2物件,也可以指定證書檔案的路徑(一般還需要提供讀取證書的密碼),還可以指定證書的儲存(Certificate Store)。我們可以利用部分方法過載提供的委託對HttpsConnectionAdapterOptions配置選項進行設定。部分方法過載還提供了一個ServerOptionsSelectionCallback委託直接返回SslServerAuthenticationOptions配置選項。
public static class ListenOptionsHttpsExtensions { public static ListenOptions UseHttps(this ListenOptions listenOptions); public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName); public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string? password); public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string? password, Action<HttpsConnectionAdapterOptions> configureOptions); public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject); public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid); public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid, StoreLocation location); public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions); public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate); public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions); public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<HttpsConnectionAdapterOptions> configureOptions); public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions); public static ListenOptions UseHttps(this ListenOptions listenOptions, ServerOptionsSelectionCallback serverOptionsSelectionCallback, object state); public static ListenOptions UseHttps(this ListenOptions listenOptions, ServerOptionsSelectionCallback serverOptionsSelectionCallback, object state, TimeSpan handshakeTimeout); public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions); } public delegate ValueTask<SslServerAuthenticationOptions> ServerOptionsSelectionCallback(SslStream stream, SslClientHelloInfo clientHelloInfo, object? state, CancellationToken cancellationToken);
除了呼叫上述這些方法來為註冊的終結點提供HTTPS相關的設定外,這些設定也可以按照如下的方式放在終結點的配置中。
{ "Kestrel": { "Endpoints": { "MyHttpsEndpoint": { "Url": "https://localhost:5001", "ClientCertificateMode": "AllowCertificate", "Certificate": { "Path": "c:\\certificates\\foobar.pfx>", "Password": "password" } } } } }
五、限制約束
為了確保KestrelServer穩定可靠地執行,需要根據需要為它設定相應的限制和約束,這些設定體現在KestrelServerOptions配置選項Limits屬性返回的KestrelServerLimits物件上。
public class KestrelServerOptions { public KestrelServerLimits Limits { get; } = new KestrelServerLimits(); } public class KestrelServerLimits { public long? MaxConcurrentConnections { get; set; } public long? MaxConcurrentUpgradedConnections { get; set; } public TimeSpan KeepAliveTimeout { get; set; } public int MaxRequestHeaderCount { get; set; } public long? MaxRequestBufferSize { get; set; } public int MaxRequestHeadersTotalSize { get; set; } public int MaxRequestLineSize { get; set; } public long? MaxRequestBodySize { get; set; } public TimeSpan RequestHeadersTimeout { get; set; } public MinDataRate MinRequestBodyDataRate { get; set; } public long? MaxResponseBufferSize { get; set; } public MinDataRate MinResponseDataRate { get; set; } public Http2Limits Http2 { get; } public Http3Limits Http3 { get; } }
KestrelServerLimits利用其豐富的屬性對連線、請求和響應進行了相應的限制。KestrelServer提供了針對HTTP 2和HTTP3的支援,針對性的限制設定體現在KestrelServerLimits型別的Http2和Http3屬性上。下表對定義在KestrelServerLimits型別中的這些屬性所體現的限制約束進行了簡單說明。
屬性 |
含 義 |
MaxConcurrentConnections |
最大併發連線。如果設定為Null(預設值),意味著不作限制。 |
MaxConcurrentUpgradedConnections |
可升級連線(比如從HTTP升級到WebSocket)的最大併發數。如果設定為Null(預設值),意味著不作限制。 |
KeepAliveTimeout |
連線保持活動狀態的超時時間,預設值為130秒。 |
MaxRequestHeaderCount |
請求攜帶的最大報頭數量,預設值為100。 |
MaxRequestBufferSize |
請求緩衝區最大容量,預設值為1,048,576位元組(1M)。 |
MaxRequestHeadersTotalSize |
請求攜帶報頭總位元組數,預設值為 32,768位元組(32K)。 |
MaxRequestLineSize |
對於HTTP 1.X來說就是請求的首行(Request Line)最大位元組數。對於HTTP 2/3來說就是 :method, :scheme, :authority, and :path這些報頭的總位元組數。預設值為8,192 位元組(8K)。 |
MaxRequestBodySize |
請求主體最大位元組數,預設值為30,000,000 位元組(約28.6M)。如果設定為Null,意味著不作限制。 |
RequestHeadersTimeout |
接收請求報頭的超時時間,預設為30秒。 |
MinRequestBodyDataRate |
請求主體內容最低傳輸率。 |
MaxResponseBufferSize |
響應緩衝區最大容量,預設值為65,536(1M)。 |
MinResponseDataRate |
響應最低傳輸率。 |
KestrelServerLimits的MinRequestBodyDataRate和MinResponseDataRate屬性返回的最低傳輸率體現為如下這個MinDataRate物件。如果沒有達到設定的傳輸率,當前連線就會被重置。MinDataRate物件除了提供表示傳輸率的BytesPerSecond屬性外,還提供了一個表示“寬限時間”的GracePeriod屬性。並非傳輸率下降到設定的閾值的那一刻就重置連線,只要在指定的時段內傳輸率上升到閾值以上也沒有問題。MinRequestBodyDataRate和MinResponseDataRate屬性的預設值均為“240 bytes/second(5 seconds)”。
public class MinDataRate { public double BytesPerSecond { get; } public TimeSpan GracePeriod { get; } public MinDataRate(double bytesPerSecond, TimeSpan gracePeriod); }
HTTP 1.X建立在TCP之上,客戶端和服務端之間的互動依賴預先建立的TCP連線。雖然HTTP 1.1引入的流水線技術允許客戶端可以隨時向服務端傳送請求,而無需等待接收到上一個請求的響應,但是響應依然只能按照請求的接收順序返回的。真正意義上的“併發”請求只能利用多個連線來完成,但是針對同一個域名支援的TCP連線的數量又是有限的。這個問題在HTTP 2得到了一定程度的解決。
與採用文字編碼的HTTP 1.X相比, HTTP 2採用更加高效的二進位制編碼。幀(Frame)成為了基本通訊單元,單個請求和響應可以分解成多個幀進行傳送。客戶端和服務端之間額訊息交換在一個支援雙向通訊的通道(Channel)中完成,該通道被稱為“流(Stream)”。每一個流具有一個唯一標識,同一個TCP連線可以承載成百上千的流。每個幀攜帶著所屬流的標識,所以它可以隨時被“亂序”傳送,接收端可以利用流的標識進行重組,所以HTTP 2在同一個TCP連線上實現了“多路複用”。
使用同一個連線傳送的請求和響應都存在很多重複的報頭,為了減少報頭內容佔據的頻寬,HTTP 2會採用一種名為HPACK的壓縮演算法對報頭文字進行編碼。HPACK會在傳送和接收端維護一個索引表來儲存編碼的文字,報頭內容在傳送前會被替換成在該表的索引,接收端這利用此索引在本地壓縮表中找到原始的內容。
public class Http2Limits { public int MaxStreamsPerConnection { get; set; } public int HeaderTableSize { get; set; } public int MaxFrameSize { get; set; } public int MaxRequestHeaderFieldSize { get; set; } public int InitialConnectionWindowSize { get; set; } public int InitialStreamWindowSize { get; set; } public TimeSpan KeepAlivePingDelay { get; set; } public TimeSpan KeepAlivePingTimeout { get; set; } }
於HTTP 2相關限制和約束的設定體現在KestrelServerLimits的Http2屬性上,該屬性返回如上所示的Http2Limits物件。下表對定義在Http2Limits型別中的這些屬性所體現的限制約束進行了簡單說明。
屬性 |
含 義 |
MaxStreamsPerConnection |
連線能夠承載的流數量,預設值為100。 |
HeaderTableSize |
HPACK報頭壓縮表的容量,預設值為4096。 |
MaxFrameSize |
幀的最大位元組數,有效值在[214~224 – 1]區間範圍內,預設值為214(16384)。 |
MaxRequestHeaderFieldSize |
最大請求報頭(含報頭名稱)的最大位元組數,預設值為214(16384)。 |
InitialConnectionWindowSize |
連線的初始化請求主體快取區的大小,有效值在[65535~231]區間範圍內,預設為131072。 |
InitialStreamWindowSize |
流的初始化請求主體快取區的大小,有效值在[65535~231]區間範圍內,預設為98304。 |
KeepAlivePingDelay |
如果服務端在該屬性設定的時間跨度內沒有接收到來自客戶端的有效幀,它會主動傳送Ping請求確定客戶端的是否保持活動狀態,預設值為1秒。 |
KeepAlivePingTimeout |
傳送Ping請求的超時時間,如果客戶端在該時限內一直處於為活動狀態,當前連線將被關閉,預設值為20秒。 |
由於HTTP 2的多路複用是在同一個TCP連線上實現的,這樣的實現並不“純粹”,因為它不可能解決由於TCP的“擁塞控制”機制導致的“隊頭阻塞(Header-Of-Line Blocking)”問題。如果希望在得到併發支援的前提下還能在低延時上有更好的作為,就不得不拋棄TCP。目前被正式確定為HTTP 3的QUIC(Quick UDP Internet Connection)就將TCP替換成了UDP。如果KestrelServer支援HTTP 3,我們可以利用KestrelServerLimits的Http3屬性返回的Http3Limits物件都限制約束進行鍼對性設定。Http3Limits只包含如下這個表示最大請求報頭位元組數的MaxRequestHeaderFieldSize屬性,它的預設值為16384。
public class Http3Limits { public int MaxRequestHeaderFieldSize { get; set;} }
六、其他設定
除了註冊的終結點和基於通訊的限制約束,KestrelServerOptions配置選項還利用如下的屬性承載著其他的設定。
public class KestrelServerOptions { public bool AddServerHeader { get; set; } public bool AllowResponseHeaderCompression { get; set; } public bool AllowSynchronousIO { get; set; } public bool AllowAlternateSchemes { get; set; } public bool DisableStringReuse { get; set; } public Func<string, Encoding> RequestHeaderEncodingSelector { get; set; } public Func<string, Encoding> ResponseHeaderEncodingSelector { get; set; } }
下表對定義在KestrelServerOptions型別中的上述這些屬性進行了簡單的說明。
屬性 |
含 義 |
AddServerHeader |
是否會在回覆的響應中自動新增“Server: Kestrel”報頭,預設值為True。 |
AllowResponseHeaderCompression |
是否允許對響應報頭進行HPACK壓縮,預設值為True。 |
AllowSynchronousIO |
是否允許對請求和響應進行同步IO操作,預設值為False,意味這個預設情況下以同步方式讀取請求和寫入響應都會丟擲異常。 |
AllowAlternateSchemes |
是否允許為“:scheme”欄位(針對HTTP 2和HTTP 3)提供一個與當前傳輸不匹配的值(“http”或者“https”),預設值為False。如果將這個屬性設定為True,意味著HttpRequest.Scheme屬性可能與採用的傳輸型別不匹配。 |
DisableStringReuse |
建立的字串是否可以在多個請求中複用。 |
RequestHeaderEncodingSelector |
用於設定某個請求報頭採用的編碼方式,預設為Utf8Encoding。 |
ResponseHeaderEncodingSelector |
用於設定某個響應報頭採用的編碼方式,預設為ASCIIEncoding。 |