天天看點

KestrelServer詳解[1]:注冊監聽終結點(Endpoint)一、UseKestrel擴充方法二、兩種終結點的取舍三、終結點配置四、針對HTTPS的設定五、限制限制六、其他設定

具有跨平台能力的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(this IWebHostBuilder hostBuilder,Action<KestrelServerOptions> options);
    public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, KestrelServerOptions> configureOptions);
}           

複制

注冊到KestrelServer上的終結點展現為如下這個Endpoint對象。Endpoint是對網絡位址的抽象,它們在大部分下展現為“IP位址+端口”或者“域名+端口”,對應的類型分别為IPEndPoint和DnsEndPoint。UnixDomainSocketEndPoint表示基于Unix Domain Socket/IPC Socket的終結點,它旨在實作同一台機器上多個程序之間的通信(IPC)。FileHandleEndPoint表示指向某個檔案句柄(比如TCP或者Pipe類型的檔案句柄)的終結點。

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擴充方法注冊的監聽位址。由于兩種情況都涉及到放棄某種設定,是以輸出了相應的日志。

KestrelServer詳解[1]:注冊監聽終結點(Endpoint)一、UseKestrel擴充方法二、兩種終結點的取舍三、終結點配置四、針對HTTPS的設定五、限制限制六、其他設定

圖1 兩種終結點的選擇

三、終結點配置

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。

本文參與 騰訊雲自媒體分享計劃 ,歡迎熱愛寫作的你一起參與!

本文分享自作者個人站點/部落格

http://www.cnblogs.com/artech/

複制

如有侵權,請聯系 [email protected] 删除。