Rake no caminho para se manter vivo

Aumentar a atividade de troca de dados entre microsserviços geralmente é um problema na arquitetura das soluções modernas de TI. Aperte o máximo e sobreviva a todo custo - um sério desafio para qualquer desenvolvimento. Portanto, a busca de soluções ideais é um processo ininterrupto. O artigo descreve brevemente os problemas que podem surgir ao usar solicitações HTTP altamente carregadas e maneiras de ignorá-las.


Esta história começa com um erro. De alguma forma, realizamos testes de carga, cujo principal elemento foi a execução de um grande número de solicitações curtas de http. Um cliente gravado no netcore 2.2 , iniciando em algum momento, lança um System.Net.Sockets.SocketException: Endereço já em uso . Logo ficou claro que as portas não tinham tempo para liberar o cliente e, em algum momento, o sistema foi recusado a abrir uma nova. Agora, se formos ao código, o problema estava no uso da abordagem antiga com a classe e construção HttpWebRequest :


var request = WebRequest.CreateHttp(uri);
using(var resp = request.GetResponse()){ … }

Parece que estamos liberando o recurso e a porta deve ser liberada em tempo hábil. No entanto, o netstat sinalizou um rápido aumento no número de portas no estado TIME_WAIT. Esse estado significa aguardar o fechamento da conexão (e possivelmente receber dados perdidos). Como resultado, a porta pode ficar nela por 1-2 minutos. Esse problema é discutido com mais detalhes em muitos artigos ( problemas com a fila TIME_WAIT , histórico sobre TIME_WAIT ). No entanto, isso significa que o dotnet está "honestamente" tentando fechar a conexão, e isso já acontece mais devido à falha das configurações de tempo limite no sistema.


Por que isso está acontecendo e como lidar com isso


keep-alive. . , . msdn, KeepAlive HttpWebRequest true. HttpWebRequest «» , , . , HttpWebRequest «Connection: keep-alive», HTTP/1.1. , , KeepAlive. HttpWebRequest.KeepAlive = false, «Connection: close». , . nginx .


:


while (true)
{
  var request = WebRequest.CreateHttp(uri);
  request.KeepAlive = false;
  var resp = await request.GetResponseAsync();
  using (var sr = new StreamReader(resp.GetResponseStream()))
  {
    var content = sr.ReadToEnd();
  }
}

, ( 1000 ) . CLOSE_WAIT, LAST_ACK. - , . , «» .


,


, , . keep-alive HttpClient. .


, , ? keep-alive nginx:


  • keepalive_timeout – ( 15)
  • keepalive_requests – ( 100)

netstat wireshark, . keepalive_requests (> 1000) , .



http , . . , , keep-alive. keep-alive , .


:


  • RunHttpClient – HttpClient "Connection: keep-alive"
  • RunHttpClientClosed – HttpClient "Connection: closed"
  • RunWebRequestClosed - usa o modo HttpWebRequest da classe "Connection: closed"

O servidor nginx está configurado com os seguintes parâmetros:


  • keepalive_timeout 60s;
  • keepalive_requests 100000;

MétodoNTheadsSignificar
Runhttpclient10001 1963,3 ms
RunWebRequestClosed10001 13.857,4 ms
RunHttpClientClosed10001 11.612,4 ms
Runhttpclient10.0001 19.573,9 ms
RunWebRequestClosed10.0001 137.947,4 ms
RunHttpClientClosed10.0001 116.112,9 ms

All Articles