Estou atrasado para a festa, mas aqui está minha jornada de aprendizado sobre este assunto complicado.
1. Onde podemos encontrar o advogado oficial da reutilização do HttpClient?
Quero dizer, se a reutilização do HttpClient é planejada
e isso é importante , esse advogado é melhor documentado em sua própria documentação da API, em vez de estar oculto em muitos "Tópicos avançados", "Padrão (anti) de desempenho" ou outras postagens do blog por aí . Caso contrário, como um novo aluno deve saber isso antes que seja tarde demais?
A partir de agora (maio de 2018), o primeiro resultado de pesquisa ao pesquisar "c # httpclient" aponta para esta página de referência da API no MSDN , que não menciona essa intenção. Bem, a lição 1 aqui para iniciantes é: sempre clique no link "Outras versões" logo após o título da página de ajuda do MSDN, você provavelmente encontrará links para a "versão atual" lá. Nesse caso HttpClient, ele o levará ao documento mais recente
aqui, contendo a descrição da intenção .
Suspeito que muitos desenvolvedores que não conheciam esse tópico também não encontraram a página de documentação correta; é por isso que esse conhecimento não é amplamente difundido e as pessoas ficaram surpresas quando descobriram mais
tarde , possivelmente da maneira mais difícil .
2. A (mis?) Concepção de using
IDisposable
Este é um pouco fora de tópico, mas ainda vale ressaltar que, não é uma coincidência ver as pessoas nessas postagens do blog culparem como HttpClient
a IDisposable
interface faz com que elas usem o using (var client = new HttpClient()) {...}
padrão e depois levem ao problema.
Eu acredito que isso se resume a uma concepção não dita (mis?):
"Espera-se que um objeto descartável seja de curta duração" .
NO ENTANTO, embora certamente pareça algo de curta duração quando escrevemos código neste estilo:
using (var foo = new SomeDisposableObject())
{
...
}
a documentação oficial sobre IDisposable
nunca menciona que os IDisposable
objetos precisam ter vida curta. Por definição, o IDisposable é apenas um mecanismo que permite liberar recursos não gerenciados. Nada mais. Nesse sentido, espera-se que você ative o descarte, mas isso não exige que você faça isso de maneira breve.
Portanto, é seu trabalho escolher adequadamente quando acionar o descarte, com base nos requisitos do ciclo de vida do seu objeto real. Não há nada que o impeça de usar um IDisposable de uma maneira duradoura:
using System;
namespace HelloWorld
{
class Hello
{
static void Main()
{
Console.WriteLine("Hello World!");
using (var client = new HttpClient())
{
for (...) { ... } // A really long loop
// Or you may even somehow start a daemon here
}
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
}
Com esse novo entendimento, agora que revisitamos a postagem do blog , podemos notar claramente que a "correção" é inicializada HttpClient
uma vez, mas nunca a elimina, é por isso que podemos ver em sua saída do netstat que a conexão permanece no estado ESTABLISHED, o que significa que NÃO foi fechado corretamente. Se estivesse fechado, seu estado estaria em TIME_WAIT. Na prática, não é grande coisa vazar apenas uma conexão aberta após todo o programa terminar, e o pôster do blog ainda vê um ganho de desempenho após a correção; mas ainda assim, é conceitualmente incorreto culpar o IDisposable e optar por NÃO descartá-lo.
3. Temos que colocar o HttpClient em uma propriedade estática ou até colocá-lo como um singleton?
Com base no entendimento da seção anterior, acho que a resposta aqui fica clara: "não necessariamente". Realmente depende de como você organiza seu código, desde que você reutilize um HttpClient AND (idealmente) o descarte eventualmente.
Hilariamente, nem mesmo o exemplo na
seção Observações do documento oficial atual
é estritamente correto. Ele define uma classe "GoodController", contendo uma propriedade estática HttpClient que não será descartada; que desobedece o que outro exemplo da seção Exemplos
enfatiza: "precisa chamar descarte ... para que o aplicativo não vaze recursos".
E, por último, o singleton não deixa de ter seus próprios desafios.
"Quantas pessoas pensam que variável global é uma boa idéia? Ninguém.
Quantas pessoas pensam que o singleton é uma boa ideia? Um pouco.
O que da? Singletons são apenas um monte de variáveis globais ".
- Citado nesta palestra inspiradora, "Estado Global e Singletons"
PS: SqlConnection
Este é irrelevante para as perguntas e respostas atuais, mas provavelmente é bom saber. O padrão de uso do SqlConnection é diferente. Você NÃO precisa reutilizar o SqlConnection , porque ele manipulará seu pool de conexões melhor dessa maneira.
A diferença é causada por sua abordagem de implementação. Cada instância HttpClient usa seu próprio conjunto de conexões (citado
aqui ); mas o próprio SqlConnection é gerenciado por um pool de conexão central, de acordo com isso .
E você ainda precisa descartar o SqlConnection, o mesmo que você deve fazer para o HttpClient.