Compartilhar via


Configuração do cliente HTTP YARP

Introdução

Cada Cluster tem uma instância dedicada de HttpMessageInvoker usada para encaminhar solicitações para os seus Destinos. A configuração é definida por cluster. Na inicialização do YRP, todos os clusters obtêm novas instâncias HttpMessageInvoker, no entanto, se posteriormente a configuração do cluster for alterada, o IForwarderHttpClientFactory será executado novamente e decidirá se deve criar um novo HttpMessageInvoker ou continuar usando o existente. A implementação IForwarderHttpClientFactory padrão cria um novo HttpMessageInvoker quando há alterações no HttpClientConfig.

As propriedades das solicitações de saída para um determinado cluster também podem ser configuradas. Eles são definidos em ForwarderRequestConfig.

A configuração é representada de forma diferente se estiveres a usar o modelo IConfiguration ou o modelo code-first.

IConfiguração

Esses tipos são focados na definição de configuração serializável. O modelo de configuração baseado em código é descrito abaixo na seção "Configuração de código".

HttpClient

A configuração do cliente HTTP é baseada em HttpClientConfig e representada pelo esquema de configuração a seguir. Se você precisar de uma abordagem mais granular, use uma implementação personalizada de IForwarderHttpClientFactory.

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

Definições de configuração:

  • SslProtocols - protocolos SSL estão ativados no fornecido cliente HTTP. Os nomes de protocolo são especificados como matriz de cadeias de caracteres. O valor padrão é Nenhum.
"SslProtocols": [
  "Tls11",
  "Tls12"
]
  • MaxConnectionsPerServer - número máximo de conexões HTTP 1.1 abertas simultaneamente para o mesmo servidor. O valor padrão é int32. MaxValue.
"MaxConnectionsPerServer": "10"
  • DangerousAcceptAnyServerCertificate - indica se a validade do certificado SSL do servidor é verificada pelo cliente. Defini-lo como true desativa completamente a validação. O valor padrão é false.
"DangerousAcceptAnyServerCertificate": "true"
  • RequestHeaderEncoding - habilita a codificação diferente de ASCII para cabeçalhos de solicitação de saída. Definir esse valor aproveitará SocketsHttpHandler.RequestHeaderEncodingSelector e usará a codificação selecionada para todos os cabeçalhos. O valor é então analisado por Encoding.GetEncoding, use valores como: "utf-8", "iso-8859-1", etc.
"RequestHeaderEncoding": "utf-8"
  • ResponseHeaderEncoding - habilita a codificação diferente de ASCII para cabeçalhos de resposta de entrada (de solicitações que o proxy encaminharia). Definir esse valor aproveitará SocketsHttpHandler.ResponseHeaderEncodingSelector e usará a codificação selecionada para todos os cabeçalhos. O valor é então analisado por Encoding.GetEncoding, use valores como: "utf-8", "iso-8859-1", etc.
"ResponseHeaderEncoding": "utf-8"

Observe que, se você estiver usando uma codificação diferente de ASCII, também precisará configurar seu servidor para aceitar solicitações e/ou enviar respostas com esses cabeçalhos. Por exemplo, ao usar Kestrel como servidor, use KestrelServerOptions.RequestHeaderEncodingSelector / .ResponseHeaderEncodingSelector para configurar Kestrel para permitir cabeçalhos Latin1 ("iso-8859-1") :

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrel =>
{
    kestrel.RequestHeaderEncodingSelector = _ => Encoding.Latin1;
    // and/or
    kestrel.ResponseHeaderEncodingSelector = _ => Encoding.Latin1;
});
  • EnableMultipleHttp2Connections - permite abrir conexões HTTP/2 adicionais para o mesmo servidor quando o número máximo de fluxos simultâneos é atingido em todas as conexões existentes. O padrão é true. Consulte SocketsHttpHandler.EnableMultipleHttp2Connections
"EnableMultipleHttp2Connections": false
  • WebProxy - Permite enviar solicitações através de um proxy HTTP de saída para chegar aos destinos. Consulte SocketsHttpHandler.Proxy para obter detalhes.
    • Endereço - O endereço url do proxy de saída.
    • BypassOnLocal - Um bool que indica se as solicitações para endereços locais devem ignorar o proxy de saída.
    • UseDefaultCredentials - Um bool que indica se as credenciais do aplicativo atual devem ser usadas para autenticar no proxy de saída. ASP.NET Core não personifica utilizadores autenticados para solicitações de saída.
"WebProxy": {
  "Address": "http://myproxy:8080",
  "BypassOnLocal": "true",
  "UseDefaultCredentials": "false"
}

HttpRequest

A configuração da solicitação HTTP é baseada em ForwarderRequestConfig e representada pelo esquema de configuração a seguir.

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

Definições de configuração:

  • ActivityTimeout - por quanto tempo é permitido que uma solicitação permaneça inativa após a conclusão de qualquer operação, depois do qual será cancelada. O padrão é 100 segundos. O tempo limite será redefinido quando os cabeçalhos de resposta forem recebidos ou depois de ler ou gravar com êxito qualquer solicitação, resposta ou dados de streaming, como gRPC ou WebSockets. Os pings de protocolo TCP keep-alives e HTTP/2 não redefinirão o tempo limite, mas os pings WebSocket sim.
  • Versão - solicitação de saída versão. Os valores suportados no momento são 1.0, 1.1, 2 e 3. O valor padrão é 2.
  • VersionPolicy - define como a versão final é selecionada para as solicitações de saída. Consulte HttpRequestMessage.VersionPolicy. O valor padrão é RequestVersionOrLower.
  • AllowResponseBuffering - permite usar buffer de gravação ao enviar uma resposta de volta para o cliente, se o servidor que hospeda o YARP (por exemplo, IIS) o suportar. NOTA: habilitá-lo pode quebrar cenários de eventos do lado do servidor (SSE).

Exemplo de configuração

O exemplo abaixo mostra 2 exemplos de configurações de cliente HTTP e solicitação para cluster1 e cluster2.

{
  "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/"
        }
      }
    }
  }
}

Configuração de código

A configuração do cliente HTTP usa o tipo HttpClientConfig.

A seguir está um exemplo de HttpClientConfig usando uma configuração de baseada em código de. A propriedade HttpClientConfig é atribuída a uma instância de antes de passar a matriz de cluster para o método 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);

Configurando o cliente http

ConfigureHttpClient fornece um callback para personalizar as configurações de SocketsHttpHandler usadas para solicitações de proxy. Isso será chamado sempre que um cluster for adicionado ou alterado. As configurações de cluster são aplicadas ao manipulador antes do callback. Dados personalizados podem ser fornecidos nos metadados do cluster. Este exemplo mostra a adição de um certificado de cliente que autenticará o proxy nos servidores de destino.

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

Fábrica de IForwarderHttpClient personalizada

Pode-se substituir o IForwarderHttpClientFactory padrão por um personalizado se for necessário o controle direto da construção do cliente HTTP. Para algumas personalizações, pode-se derivar do ForwarderHttpClientFactory padrão e substituir os métodos que configuram o cliente.

É recomendável que qualquer fábrica personalizada defina as seguintes propriedades SocketsHttpHandler com os mesmos valores que a fábrica padrão para preservar um comportamento correto de proxy reverso e evitar sobrecarga desnecessária.

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

Sempre retorne uma instância HttpMessageInvoker em vez de uma instância HttpClient que deriva de HttpMessageInvoker. HttpClient armazena respostas em buffer por padrão, o que quebra cenários de streaming e aumenta o uso de memória e latência.

Dados personalizados podem ser fornecidos nos metadados do cluster.

A seguir está um exemplo de uma implementação de IForwarderHttpClientFactory personalizada.

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);
    }
}