Qual deve ser a HttpClient
vida útil de um cliente WebAPI?
É melhor ter uma instância do HttpClient
para várias chamadas?
Qual é a sobrecarga de criar e descartar uma HttpClient
solicitação por, como no exemplo abaixo (extraído de http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from- a-net-client ):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:9000/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// New code:
HttpResponseMessage response = await client.GetAsync("api/products/1");
if (response.IsSuccessStatusCode)
{
Product product = await response.Content.ReadAsAsync<Product>();
Console.WriteLine("{0}\t${1}\t{2}", product.Name, product.Price, product.Category);
}
}
c#
asp.net
web-services
asp.net-web-api
dotnet-httpclient
Bruno Pessanha
fonte
fonte
Stopwatch
classe para compará-la, no entanto. Minha estimativa seria que faz mais sentido ter um únicoHttpClient
, supondo que todas essas instâncias sejam usadas no mesmo contexto.Respostas:
HttpClient
foi projetado para ser reutilizado para várias chamadas . Mesmo em vários segmentos. OHttpClientHandler
credenciais e Cookies que se destinam a ser entre as chamadas reutilizado. Ter uma novaHttpClient
instância requer a restauração de todas essas coisas. Além disso, aDefaultRequestHeaders
propriedade contém propriedades destinadas a várias chamadas. Ter que redefinir esses valores em cada solicitação anula o ponto.Outro grande benefício
HttpClient
é a capacidade de adicionarHttpMessageHandlers
ao pipeline de solicitação / resposta para aplicar preocupações transversais. Estes podem ser para registro, auditoria, limitação, manipulação de redirecionamento, manipulação offline, captura de métricas. Todos os tipos de coisas diferentes. Se um novo HttpClient for criado em cada solicitação, todos esses manipuladores de mensagens precisarão ser configurados em cada solicitação e, de alguma forma, qualquer estado de nível de aplicativo compartilhado entre solicitações para esses manipuladores também precisará ser fornecido.Quanto mais você usar os recursos
HttpClient
, mais verá que reutilizar uma instância existente faz sentido.No entanto, o maior problema, na minha opinião, é que, quando uma
HttpClient
classe é descartada, ela é descartada, oHttpClientHandler
que fecha forçosamente aTCP/IP
conexão no pool de conexões gerenciadas porServicePointManager
. Isso significa que cada solicitação com uma novaHttpClient
requer o restabelecimento de uma novaTCP/IP
conexão.Nos meus testes, usando HTTP simples em uma LAN, o impacto no desempenho é bastante insignificante. Suspeito que isso ocorra porque existe um keepalive TCP subjacente que mantém a conexão aberta mesmo quando
HttpClientHandler
tenta fechá-la.Em solicitações que passam pela Internet, eu vi uma história diferente. Vi um impacto de 40% no desempenho devido à necessidade de reabrir a solicitação todas as vezes.
Eu suspeito que o golpe em uma
HTTPS
conexão seria ainda pior.Meu conselho é manter uma instância do HttpClient durante a vida útil do seu aplicativo para cada API distinta à qual você se conecta.
fonte
which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager
Você tem certeza sobre esta afirmação? Isso é difícil de acreditar.HttpClient
parece-me uma unidade de trabalho que deveria ser instanciada com frequência.Se você deseja que seu aplicativo seja dimensionado, a diferença é ENORME! Dependendo da carga, você verá números de desempenho muito diferentes. Como menciona Darrel Miller, o HttpClient foi projetado para ser reutilizado em solicitações. Isso foi confirmado pelos caras da equipe da BCL que o escreveram.
Um projeto recente que tive foi ajudar um varejista de computadores on-line muito grande e conhecido a expandir o tráfego da Black Friday / feriado para alguns novos sistemas. Encontramos alguns problemas de desempenho relacionados ao uso do HttpClient. Como ele é implementado
IDisposable
, os desenvolvedores fizeram o que você normalmente faria criando uma instância e colocando-a dentro de umausing()
instrução. Depois que começamos a testar o aplicativo, o aplicativo ficou de joelhos - sim, o servidor não apenas o aplicativo. O motivo é que todas as instâncias do HttpClient abrem uma porta no servidor. Devido à finalização não determinística do GC e ao fato de você estar trabalhando com recursos de computador que abrangem várias camadas OSI , o fechamento das portas de rede pode demorar um pouco. De fato, o próprio sistema operacional Windowspode levar até 20 segundos para fechar uma porta (por Microsoft). Estávamos abrindo portas mais rapidamente do que podiam ser fechadas - a exaustão da porta do servidor que aumentou a CPU a 100%. Minha correção foi alterar o HttpClient para uma instância estática que resolveu o problema. Sim, é um recurso descartável, mas qualquer sobrecarga é amplamente compensada pela diferença de desempenho. Convido você a fazer alguns testes de carga para ver como seu aplicativo se comporta.Você também pode verificar a página de Orientação da WebAPI para obter documentação e exemplo em https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client
Preste atenção especial a esta chamada:
Se você achar que precisa usar uma estática
HttpClient
com diferentes cabeçalhos, endereço base etc., o que você precisará fazer é criar oHttpRequestMessage
manual e definir esses valores noHttpRequestMessage
. Então, use oHttpClient:SendAsync(HttpRequestMessage requestMessage, ...)
ATUALIZAÇÃO para .NET Core : você deve usar a
IHttpClientFactory
injeção de dependência via para criarHttpClient
instâncias. Ele gerenciará a vida útil para você e você não precisará descartá-lo explicitamente. Consulte Fazer solicitações HTTP usando IHttpClientFactory no ASP.NET Corefonte
Como as outras respostas afirmam,
HttpClient
destina-se à reutilização. No entanto, reutilizar uma únicaHttpClient
instância em um aplicativo multithread significa que você não pode alterar os valores de suas propriedades com estado, comoBaseAddress
eDefaultRequestHeaders
(portanto, você pode usá-los apenas se eles forem constantes em seu aplicativo).Uma abordagem para contornar essa limitação é agrupar
HttpClient
uma classe que duplica todos osHttpClient
métodos necessários (GetAsync
,PostAsync
etc) e os delega a um singletonHttpClient
. No entanto, isso é muito tedioso (você também precisará envolver os métodos de extensão ) e, felizmente, existe outra maneira - continue criando novasHttpClient
instâncias, mas reutilize as subjacentesHttpClientHandler
. Apenas certifique-se de não descartar o manipulador:fonte
SendAsync
é muito menos conveniente do que os métodos específicos, tais comoPutAsync
,PostAsJsonAsync
etcRelacionado a sites de alto volume, mas não diretamente ao HttpClient. Temos o trecho de código abaixo em todos os nossos serviços.
Em https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2); k (DevLang-csharp) & rd = true
"Você pode usar esta propriedade para garantir que as conexões ativas de um objeto ServicePoint não permaneçam abertas indefinidamente. Essa propriedade é destinada a cenários em que as conexões devem ser descartadas e restabelecidas periodicamente, como cenários de balanceamento de carga.
Por padrão, quando KeepAlive é verdadeiro para uma solicitação, a propriedade MaxIdleTime define o tempo limite para o fechamento de conexões do ServicePoint devido à inatividade. Se o ServicePoint tiver conexões ativas, o MaxIdleTime não terá efeito e as conexões permanecerão abertas indefinidamente.
Quando a propriedade ConnectionLeaseTimeout é definida como um valor diferente de -1 e após o tempo especificado, uma conexão ativa do ServicePoint é fechada após atender a uma solicitação, configurando KeepAlive como false nessa solicitação. A configuração desse valor afeta todas as conexões gerenciadas pelo objeto ServicePoint. "
Quando você tem serviços atrás de uma CDN ou outro ponto de extremidade que deseja fazer failover, essa configuração ajuda os chamadores a segui-lo até seu novo destino. Neste exemplo, 60 segundos após um failover, todos os chamadores devem se reconectar ao novo terminal. Requer que você conheça seus serviços dependentes (aqueles que você chama) e seus pontos de extremidade.
fonte
Você também pode consultar esta publicação no blog de Simon Timms: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
fonte
Uma coisa a salientar, que nenhum dos blogs "não use" é que não é apenas o BaseAddress e o DefaultHeader que você precisa considerar. Depois de tornar o HttpClient estático, há estados internos que serão transportados pelas solicitações. Um exemplo: você está se autenticando com um terceiro no HttpClient para obter um token FedAuth (ignore por que não usa OAuth / OWIN / etc), essa mensagem de resposta possui um cabeçalho Set-Cookie para FedAuth, que é adicionado ao seu estado HttpClient. O próximo usuário a fazer login na sua API enviará o cookie FedAuth da última pessoa, a menos que você esteja gerenciando esses cookies em cada solicitação.
fonte
Como primeira edição, enquanto essa classe é descartável, usá-la com a
using
declaração não é a melhor opção, porque mesmo quando você descartaHttpClient
objeto, o soquete subjacente não é liberado imediatamente e pode causar um problema sério chamado exaustão de soquetes.Mas há um segundo problema
HttpClient
que você pode ter ao usá-lo como objeto único ou estático. Nesse caso, um singleton ou estáticoHttpClient
não respeitaDNS
alterações.No núcleo .net, você pode fazer o mesmo com o HttpClientFactory, algo assim:
ConfigureServices
documentação e exemplo aqui
fonte