proxy reverso nginx aumenta muito a latência do pior caso

3

(editar: parcialmente compreendido e contornado, veja o comentário)

Eu tenho uma configuração com o nginx agindo como um proxy reverso na frente de um servidor de aplicativos CherryPy. Eu estou usando ab para comparar o desempenho passando por nginx vs. não, e percebendo que o primeiro caso tem um pior desempenho pior:

$ ab -n 200 -c 10 'http://localhost/noop'
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests


Server Software:        nginx
Server Hostname:        localhost
Server Port:            80

Document Path:          /noop
Document Length:        0 bytes

Concurrency Level:      10
Time taken for tests:   3.145 seconds
Complete requests:      200
Failed requests:        0
Write errors:           0
Total transferred:      29600 bytes
HTML transferred:       0 bytes
Requests per second:    63.60 [#/sec] (mean)
Time per request:       157.243 [ms] (mean)
Time per request:       15.724 [ms] (mean, across all concurrent requests)
Transfer rate:          9.19 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:     5   48 211.7     31    3007
Waiting:        5   48 211.7     31    3007
Total:          5   48 211.7     31    3007

Percentage of the requests served within a certain time (ms)
  50%     31
  66%     36
  75%     39
  80%     41
  90%     46
  95%     51
  98%     77
  99%    252
 100%   3007 (longest request)
$ ab -n 200 -c 10 'http://localhost:8080/noop'
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Finished 200 requests


Server Software:        CherryPy/3.2.0
Server Hostname:        localhost
Server Port:            8080

Document Path:          /noop
Document Length:        0 bytes

Concurrency Level:      10
Time taken for tests:   0.564 seconds
Complete requests:      200
Failed requests:        0
Write errors:           0
Total transferred:      27600 bytes
HTML transferred:       0 bytes
Requests per second:    354.58 [#/sec] (mean)
Time per request:       28.202 [ms] (mean)
Time per request:       2.820 [ms] (mean, across all concurrent requests)
Transfer rate:          47.79 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.7      0      11
Processing:     6   26  23.5     24     248
Waiting:        3   25  23.6     23     248
Total:          6   26  23.4     24     248

Percentage of the requests served within a certain time (ms)
  50%     24
  66%     27
  75%     29
  80%     31
  90%     34
  95%     40
  98%     51
  99%    234
 100%    248 (longest request)

O que poderia estar causando isso? A única coisa em que consigo pensar é que o nginx está enviando pedidos para o backend em uma ordem diferente da que eles chegaram, mas isso parece implausível.

A máquina é uma instância EC2 c1.medium com 2 núcleos, CherryPy usa um pool de threads com 10 threads e nginx tem worker_connections = 1024.

UPDATE: Mais duas descobertas confusas:

  • Em uma dada simultaneidade, o envio de mais solicitações melhora o desempenho. Com uma simultaneidade de 40 e 40 solicitações, recebo um tempo médio de 3s e máximo de 10,5s; Com uma simultaneidade de 40 e 200 solicitações, recebo uma mediana de 38ms (!) e max 7.5s. De fato, o tempo total é menor para 200 pedidos! (6,5 vs 7,5s para 40). Isso tudo é repetitivo.
  • O monitoramento de ambos os processos de trabalho do nginx com strace melhora bastante o desempenho deles, por exemplo, um tempo médio de 3 a 77 ms, sem alterar visivelmente seu comportamento. (Eu testei com uma chamada de API não trivial e confirmei que strace não altera a resposta, assim como todas essas observações de desempenho ainda estão sendo mantidas.) Isso também é repetível.
por npt 27.07.2012 / 00:48

2 respostas

4

O pior cenário de 3 segundos na sua primeira execução de ab parece uma perda de pacote. Provavelmente é o resultado de alguns buffers / recursos insuficientes configurados, algumas possíveis causas em nenhuma ordem específica:

  • Fila de escuta muito pequena em um back-end resultando em estouros de fila de escuta ocasionais (o Linux geralmente é configurado para soltar o pacote SYN nesse caso, tornando-o indiferenciável de uma perda de pacote; consulte netstat -s | grep listen para descobrir se é o problema ).
  • Firewall com estado no host local aproximando-se do limite de número de estados e descartando alguns pacotes SYN aleatórios devido a isso.
  • O sistema está sem sockets / portas locais devido a sockets no estado TIME_WAIT, consulte esta questão se você estiver usando o Linux.

Você tem que examinar seu sistema operacional cuidadosamente para descobrir a causa e configurar seu sistema operacional de acordo. Você também pode querer seguir algum guia de ajuste do subsistema de rede para o seu sistema operacional. Observe que o EC2 pode ser um pouco específico aqui, já que havia relatórios sobre o desempenho de rede muito limitado em instâncias do EC2.

Do ponto de vista do nginx, qualquer solução estaria mais ou menos errada (já que o problema não está no nginx, mas sim no sistema operacional que não pode lidar com o carregamento e descarta pacotes). No entanto, você pode tentar alguns truques para reduzir a carga no subsistema de rede do SO:

  • Configure as conexões keepalive com um back-end .
  • Configure o backend para escutar em um soquete de domínio unix (se seu backend o suportar) e configure o nginx para solicitações de proxy para ele.
por 22.08.2012 / 11:14
0

O NGINX usa HTTP / 1.0 para conexões de backend e não tem manutenção de atividade por padrão (veja link na postagem do Maxim para backend keepalive), então isso significa fazer uma nova conexão de backend para cada requisição, aumentando um pouco a latência. Provavelmente, você também deve ter mais processos de trabalho, 2 * o número de núcleos de CPU, com um mínimo de 5. Se você tiver mais de 10 solicitações simultâneas, poderá precisar de mais threads no CherryPy também.

    
por 05.09.2012 / 19:56