YARP HTTP 客户端配置

介绍

每个 群集 都有一个专用 HttpMessageInvoker 实例,用于将请求转发到其 目标。 配置是按每个群集定义的。 在 YARP 启动时,所有群集都会获得新的 HttpMessageInvoker 实例,但如果以后群集配置发生更改,则 IForwarderHttpClientFactory 将重新运行并决定是否应创建新的 HttpMessageInvoker 或使用现有实例。 当 IForwarderHttpClientFactory发生更改时,默认 HttpMessageInvoker 实现会创建新的

还可以配置给定群集的传出请求的属性。 它们在 ForwarderRequestConfig中定义。

如果使用 IConfiguration 模型或代码优先模型,则配置以不同的方式表示。

IConfiguration

这些类型侧重于定义可序列化的配置。 以下“代码配置”部分介绍了基于代码的配置模型。

HttpClient

HTTP 客户端配置基于 HttpClientConfig,并由以下配置架构表示。 如果你需要更精细的方法,请使用 IForwarderHttpClientFactory

"HttpClient": {
  "SslProtocols": [ "<protocol-names>" ],
  "MaxConnectionsPerServer": "<int>",
  "DangerousAcceptAnyServerCertificate": "<bool>",
  "RequestHeaderEncoding": "<encoding-name>",
  "ResponseHeaderEncoding": "<encoding-name>",
  "EnableMultipleHttp2Connections": "<bool>",
  "WebProxy": {
    "Address": "<url>",
    "BypassOnLocal": "<bool>",
    "UseDefaultCredentials": "<bool>"
  }
}

配置设置:

"SslProtocols": [
  "Tls11",
  "Tls12"
]
  • MaxConnectionsPerServer - 同时对同一服务器开放的最大 HTTP 1.1 连接数。 默认值为 int32 。MaxValue
"MaxConnectionsPerServer": "10"
  • DangerousAcceptAnyServerCertificate - 指示客户端是否检查了服务器 SSL 证书的有效性。 将其设置为 true 完全禁用验证。 默认值为 false
"DangerousAcceptAnyServerCertificate": "true"
"RequestHeaderEncoding": "utf-8"
  • ResponseHeaderEncoding - 启用非 ASCII 编码以处理传入的响应标头(来自代理将转发的请求)。 设置此值将利用 SocketsHttpHandler.ResponseHeaderEncodingSelector 并为所有标头使用所选编码。 然后,该值由 Encoding.GetEncoding进行解析,可以使用例如:“utf-8”、“iso-8859-1”等值。
"ResponseHeaderEncoding": "utf-8"

请注意,如果使用 ASCII 以外的编码,则还需要将服务器设置为接受具有此类标头的请求和/或发送响应。 例如,使用 Kestrel 作为服务器时,请使用 KestrelServerOptions.RequestHeaderEncodingSelector / .ResponseHeaderEncodingSelector 配置 Kestrel 以允许 Latin1(“iso-8859-1”)标头:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrel =>
{
    kestrel.RequestHeaderEncodingSelector = _ => Encoding.Latin1;
    // and/or
    kestrel.ResponseHeaderEncodingSelector = _ => Encoding.Latin1;
});
"EnableMultipleHttp2Connections": false
  • WebProxy - 允许通过出站 HTTP 代理发送请求以访问目标。 有关详细信息,请参阅 SocketsHttpHandler.Proxy
    • 地址 - 出站代理的 URL 地址。
    • BypassOnLocal - 指示对本地地址的请求是否应绕过出站代理的布尔值。
    • UseDefaultCredentials - 一个布尔值,指示当前应用程序凭据是否应用于对出站代理进行身份验证。 在出站请求中,ASP.NET Core 不会模拟经过身份验证的用户。
"WebProxy": {
  "Address": "http://myproxy:8080",
  "BypassOnLocal": "true",
  "UseDefaultCredentials": "false"
}

HttpRequest

HTTP 请求配置基于 ForwarderRequestConfig,并由以下配置架构表示。

"HttpRequest": {
  "ActivityTimeout": "<timespan>",
  "Version": "<string>",
  "VersionPolicy": ["RequestVersionOrLower", "RequestVersionOrHigher", "RequestVersionExact"],
  "AllowResponseBuffering": "<bool>"
}

配置设置:

  • ActivityTimeout - 允许请求在任何操作完成之间保持空闲的最长时间,超过此时间后请求将被取消。 默认值为 100 秒。 收到响应标头或成功读取或写入任何请求、响应或流数据(如 gRPC 或 WebSocket)后,超时将重置。 TCP 保持连接和 HTTP/2 协议 ping 将不会重置超时,但 WebSocket ping 将会重置超时。
  • 版本 - 传出请求版本。 目前支持的值为 1.01.123。 默认值为 2。
  • VersionPolicy - 定义如何为传出请求选择最终版本。 请参阅 HttpRequestMessage.VersionPolicy。 默认值为 RequestVersionOrLower
  • AllowResponseBuffering - 如果托管 YARP(例如 IIS)的服务器支持,则允许在将响应发送回客户端时使用写入缓冲。 注意:启用它可能会破坏 SSE(服务器端事件)场景。

配置示例

以下示例显示了 2 个 HTTP 客户端和 cluster1cluster2的请求配置示例。

{
  "Clusters": {
    "cluster1": {
      "LoadBalancingPolicy": "Random",
      "HttpClient": {
        "SslProtocols": [
          "Tls11",
          "Tls12"
        ],
        "MaxConnectionsPerServer": "10",
        "DangerousAcceptAnyServerCertificate": "true"
      },
      "HttpRequest": {
        "ActivityTimeout": "00:00:30"
      },
      "Destinations": {
        "cluster1/destination1": {
          "Address": "https://localhost:10000/"
        },
        "cluster1/destination2": {
          "Address": "http://localhost:10010/"
        }
      }
    },
    "cluster2": {
      "HttpClient": {
        "SslProtocols": [
          "Tls12"
        ]
      },
      "HttpRequest": {
        "Version": "1.1",
        "VersionPolicy": "RequestVersionExact"
      },
      "Destinations": {
        "cluster2/destination1": {
          "Address": "https://localhost:10001/"
        }
      }
    }
  }
}

代码配置

HTTP 客户端配置使用 HttpClientConfig类型。

下面是一个使用HttpClientConfig的配置的 示例。 在将群集数组传递给 HttpClientConfig 方法之前, 实例分配给 LoadFromMemory 属性。

var routes = new[]
{
    new RouteConfig()
    {
        RouteId = "route1",
        ClusterId = "cluster1",
        Match =
        {
            Path = "{**catch-all}"
        }
    }
};
var clusters = new[]
{
    new ClusterConfig()
    {
        ClusterId = "cluster1",
        Destinations =
        {
            { "destination1", new DestinationConfig() { Address = "https://localhost:10000" } }
        },
        HttpClient = new HttpClientConfig { MaxConnectionsPerServer = 10, SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12 }
    }
};

services.AddReverseProxy()
    .LoadFromMemory(routes, clusters);

配置 http 客户端

ConfigureHttpClient 提供一个回调函数以自定义用于代理请求的 SocketsHttpHandler 设置。 每次添加或更改群集时都会调用此项。 回调之前,群集设置会应用于处理程序。 可以在群集元数据中提供自定义数据。 此示例演示如何将代理进行身份验证的客户端证书添加到目标服务器。

var clientCert = new X509Certificate2("path");
services.AddReverseProxy()
    .ConfigureHttpClient((context, handler) =>
    {
        handler.SslOptions.ClientCertificates.Add(clientCert);
    })

自定义 IForwarderHttpClientFactory

如果需要直接控制 HTTP 客户端构造,则可以将默认 IForwarderHttpClientFactory 替换为自定义客户端构造。 对于某些自定义项,你可以从默认的 ForwarderHttpClientFactory 中派生,并替代配置客户端的方法。

建议任何自定义工厂将以下 SocketsHttpHandler 属性设置为与默认工厂相同的值,以便保留正确的反向代理行为并避免不必要的开销。

new SocketsHttpHandler
{
    UseProxy = false,
    AllowAutoRedirect = false,
    AutomaticDecompression = DecompressionMethods.None,
    UseCookies = false,
    EnableMultipleHttp2Connections = true,
    ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
    ConnectTimeout = TimeSpan.FromSeconds(15),
};

始终返回 HttpMessageInvoker 实例,而不是派生自 HttpMessageInvoker 的 HttpClient 实例。 默认情况下,HttpClient 会缓冲响应,这会中断流式处理方案,并增加内存使用率和延迟。

可以在群集元数据中提供自定义数据。

下面是自定义 IForwarderHttpClientFactory 实现的示例。

public class CustomForwarderHttpClientFactory : IForwarderHttpClientFactory
{
    public HttpMessageInvoker CreateClient(ForwarderHttpClientContext context)
    {
        var handler = new SocketsHttpHandler
        {
            UseProxy = false,
            AllowAutoRedirect = false,
            AutomaticDecompression = DecompressionMethods.None,
            UseCookies = false,
            EnableMultipleHttp2Connections = true,
            ActivityHeadersPropagator = new ReverseProxyPropagator(DistributedContextPropagator.Current),
            ConnectTimeout = TimeSpan.FromSeconds(15),
        };

        return new HttpMessageInvoker(handler, disposeHandler: true);
    }
}