.NET Core - A classe HttpClient (C#) - II


  No artigo de hoje vou apresentar os recursos da classe HttpClient para .NET Core: tratando exceções.

Continuando o artigo anterior vamos agora ver como tratar exceções e como tratar o problema das conexões quando usamos a classe HttpClient com o bloco using.

No nosso exemplo a classe HttpClient fez uma chamada ao método GetAsync com a palavra-chave await, e a thread chamada poderia fazer algum outro trabalho. Quando o resultado estiver disponível a thread  continua com a execução do método, e a resposta é retornada.

Acontece que invocar o método GetAsync da classe HttpClient por padrão não gera uma exceção se o método falhar.

Podemos então alterar isso chamando o método EnsureSuccessStatusCode com o HttpResponseMessage. Este método verifica se IsSuccessStatusCode é falso e lança uma exceção.

Lembrando que as principais exceções que o método GetAsync() pode lançar (dependendo da sobrecarga usada) são:

Um exemplo básico de código mostrando o tratamento da exceção usando esta abordagem pode ser visto a seguir no método GetDadosComExcecaoWebServiceAsync():

Agora veremos outro problema que pode ocorrer com a classe HttpClient.

O problema do using com HttpClient

Em geral quando criamos uma instância de um objeto que implementa IDisposable usamos a instrução no bloco using; assim quando a execução do bloco terminar o objeto será descartado.

using(var cliente = new HttpClient())
{
    //faz alguma coisa
}

Esse recurso é muito difundido sendo quase um padrão para desenvolvedores .NET.

Só que tem um pequeno problema: a classe HttpClient tem um comportamento diferente...

Embora a classe HttpClient implemente a interface IDisposable, ela é um objeto compartilhado, e, isso significa que, por baixo dos panos, ela é thread safe. Ao invés de criar uma nova instância de HttpClient para cada execução, será feito um compartilhamento de uma única instância do HttpClient durante toda a vida útil da aplicação.

Vamos criar uma nova aplicação .NET Core do tipo Console para mostrar isso na prática.

Abra a janela de comandos e crie um projeto na pasta HttpClientUsing :

Abra o projeto no Visual Studio Code e inclua o código abaixo no arquivo Program.cs no método Main():

Nota: Você tem que usar a versão 7.1 da linguagem C#. Para isso abra o arquivo de projeto e inclua o código abaixo:

Executando o projeto iremos obter o seguinte resultado:

Aparentemente o programa executou e foi encerrado sem problemas.

Mas se você espiar as conexões usando o comando : NETSTAT.EXE vai obter o seguinte:

Percebeu o problema ???

Embora o aplicativo foi encerrado, ainda existem muitas conexões abertas para máquina que hospeda o site para onde fizemos as requisições.

Todas elas estão no estado TIME_WAIT, o que significa que a conexão foi fechada de um lado (do nosso lado), mas ainda estamos aguardando para ver se algum pacote adicional aparece, porque pode ter sido atrasado na rede em algum lugar.

Acontece que o Windows vai manter a conexão neste estado por 240 segundos. Isso esta definido em [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay]).

Assim, existe um limite para que o Windows possa abrir novos soquetes; por isso se você esgotar o pool de conexões, os erros do tipo : "Unable do connect to the remote server...",  vão começar a aparece...

Mas como resolver esse problema ???

A solução mais recomendada é compartilhar uma única instância da classe HttpClient para reduzir o desperdício dos soquetes, reutilizando-os.

Para o exemplo usado, o código ajustado ficaria assim:

Observe que agora temos apenas uma instância do HttpClient compartilhada para o aplicativo inteiro.

A aplicação vai funcionar da mesma forma, sendo inclusiva um pouco mais rápida, devido à reutilização dos sockets.

Para saber mais sobre esse problema veja o artigo original em : https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

Pegue o projeto aqui :  HttpClientUsing.zip

"Porque o Senhor é justo, e ama a justiça; o seu rosto olha para os retos."
Salmos 11:7

Referências:


José Carlos Macoratti