Nginx proxy_read_timeout vs. proxy_connect_timeout

15

Comecei a usar o Nginx como proxy reverso para um conjunto de servidores que fornecem algum tipo de serviço.

O serviço pode ser bastante lento às vezes (sua execução no Java e na JVM às vezes fica presa na "coleta de lixo completa" que pode levar vários segundos), então eu configurei o valor proxy_connect_timeoutpara 2 segundos, o que dará ao Nginx tempo suficiente para descobrir se o serviço está parado no GC e não responde a tempo, deve passar a solicitação para um servidor diferente.

Também definai proxy_read_timeoutpara impedir que o proxy reverso seja bloqueado se o serviço em si demorar muito para calcular a resposta - novamente, ele deve mover a solicitação para outro servidor que seja livre o suficiente para retornar uma resposta oportuna.

Eu executei alguns benchmarks e posso ver claramente que isso proxy_connect_timeoutfunciona corretamente, pois algumas solicitações retornam exatamente no tempo especificado para o tempo limite da conexão, pois o serviço está parado e não aceita conexões de entrada (o serviço está usando o Jetty como um incorporado contêiner de servlet). O proxy_read_timeouttambém funciona, como posso ver os pedidos que retornam após o tempo limite especificado lá.

O problema é que eu esperava ver algumas solicitações que atingissem o tempo limite após proxy_read_timeout + proxy_connect_timeout, ou quase esse período, se o serviço estiver travado e não aceitar conexões quando o Nginx tentar acessá-lo, mas antes que o Nginx possa atingir o tempo limite - ele será liberado e inicia o processamento, mas é muito lento e o Nginx seria interrompido devido ao tempo limite da leitura. Acredito que o serviço tenha esses casos, mas depois de executar vários benchmarks, totalizando vários milhões de solicitações - não consegui ver uma única solicitação que retorne em qualquer coisa acima proxy_read_timeout(que é o tempo limite maior).

Gostaria de receber qualquer comentário sobre esse problema, embora eu ache que isso possa ter ocorrido devido a um bug no Nginx (eu ainda não verifiquei o código, então isso é apenas uma suposição) de que o contador de tempo limite não é redefinido após a conexão for bem-sucedido, se o Nginx não leu nada do servidor upstream.

Guss
fonte
1
Qual versão do NGINX? Acho que me lembro de algo semelhante em uma versão mais antiga (cerca de 0,6 / 7, talvez), mas foi corrigido em uma versão mais recente (a última versão estável é a 1.0.5), mas isso pode estar errado. Ainda sabendo a sua versão ajudaria
Smudge
Observe que os documentos dizem que proxy_read_timeoutnão é o "tempo limite global", mas entre duas operações de leitura.
Pogh 20/08/11
@ Sam: Estou usando o Nginx 1.0.0. @ Poige - Sim, estou ciente disso, e é por isso que espero que o tempo total seja proxy_read_timeout + proxy_connect_timeout.
Guss
1
Como uma observação lateral, você provavelmente deve pesquisar alguns ajustes simultâneos de coleta de lixo para a sua JVM: en.wikipedia.org/wiki/…
polynomial
@polynomial: fizemos mas de acordo com nossos testes o recurso de coleta de lixo simultâneos resulta em mais tempo de CPU perdeu para GC global em comparação a "parar o mundo" GC, portanto, preferimos investir em Nginx sintonia :-)
Guss

Respostas:

18

Na verdade, não consegui reproduzir isso em:

2011/08/20 20:08:43 [notice] 8925#0: nginx/0.8.53
2011/08/20 20:08:43 [notice] 8925#0: built by gcc 4.1.2 20080704 (Red Hat 4.1.2-48)
2011/08/20 20:08:43 [notice] 8925#0: OS: Linux 2.6.39.1-x86_64-linode19

Eu configurei isso no meu nginx.conf:

proxy_connect_timeout   10;
proxy_send_timeout      15;
proxy_read_timeout      20;

Eu, então, configurei dois servidores de teste. Um que acabaria com o tempo limite no SYN e outro que aceitaria conexões, mas nunca responderia:

upstream dev_edge {
  server 127.0.0.1:2280 max_fails=0 fail_timeout=0s; # SYN timeout
  server 10.4.1.1:22 max_fails=0 fail_timeout=0s; # accept but never responds
}

Então enviei uma conexão de teste:

[m4@ben conf]$ telnet localhost 2480
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Host: localhost

HTTP/1.1 504 Gateway Time-out
Server: nginx
Date: Sun, 21 Aug 2011 03:12:03 GMT
Content-Type: text/html
Content-Length: 176
Connection: keep-alive

Então assisti error_log que mostrava isso:

2011/08/20 20:11:43 [error] 8927#0: *1 upstream timed out (110: Connection timed out) while connecting to upstream, client: 127.0.0.1, server: ben.dev.b0.lt, request: "GET / HTTP/1.1", upstream: "http://10.4.1.1:22/", host: "localhost"

então:

2011/08/20 20:12:03 [error] 8927#0: *1 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: ben.dev.b0.lt, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:2280/", host: "localhost"

E então o access.log que tem o tempo limite esperado de 30s (10 + 20):

504:32.931:10.003, 20.008:.:176 1 127.0.0.1 localrhost - [20/Aug/2011:20:12:03 -0700] "GET / HTTP/1.1" "-" "-" "-" dev_edge 10.4.1.1:22, 127.0.0.1:2280 -

Aqui está o formato de log que estou usando, que inclui os tempos limite de upstream individuais:

log_format  edge  '$status:$request_time:$upstream_response_time:$pipe:$body_bytes_sent $connection $remote_addr $host $remote_user [$time_local] "$request" "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $edge $upstream_addr $upstream_cache_status';
polinomial
fonte
1
Minha pergunta acima, no seu cenário, é mais ou menos assim: suponha que um servidor de teste que aceite a conexão após um tempo aleatório entre 0 e 20 segundos e aguarde um tempo aleatório entre 19 e 21 segundos antes de responder. Em seguida, execute uma referência simples contra ele. Eu esperaria ver cerca de 50% dos pedidos com um tempo limite de 10 segundos, 25% com um tempo limite de 20 a 30 segundos e 25% receberão uma resposta bem-sucedida. Nesse caso, quantas solicitações bem-sucedidas levarão mais de 20 segundos para serem concluídas? Na minha referência, nenhum deles é - e isso me incomoda.
Guss
Eu testei configurando perdas aleatórias no SYN e depois tendo um CGI que cospe linhas muito lentamente por cerca de 50 segundos. Consegui ver as solicitações demorando muito mais do que os dois tempos limite combinados, mas ainda tendo êxito: box.access.log 200: 69.814: 67.100:.: 1579 33 127.0.0.1 test.host - [21 / ago / 2011: 20: 30:52 -0700] "GET / huugs HTTP / 1.1" "-" "-" "-" dev_edge 127.0.0.1:2280 -
polinômio
Ok, isso é estranho em um nível totalmente diferente :-). Uma explicação possível é que o Nginx leva um tempo para escrever a solicitação ( proxy_send_timeout) e, como você a definiu para um valor mais alto proxy_connection_timeout, isso pode ser responsável por qualquer atraso nos 20 segundos proxy_read_timeout. Quando você diz "cuspir linhas muito lentamente" - o que você quer dizer?
Guss
sleep 1 entre linhas de impressão HTML no corpo da resposta. Apenas expondo como o proxy_read_timeout está entre as leituras, não a leitura inteira.
polinomial
1
Ah entendo. Bem, esse definitivamente não é o meu caso e desculpe-me por não ter deixado claro no meu OP. No meu caso, o servidor de aplicativos conclui todo o processamento antes de retornar qualquer tipo de resposta e retorna tudo de uma só vez - para proxy_read_timeoutque a solicitação falhe completamente ou permita completamente. Isso também explica a diferença entre o comportamento que você vê e o que eu vejo.
Guss
3

O problema é que eu esperava ver algumas solicitações que atingissem o tempo limite após proxy_read_timeout + proxy_connect_timeout, ou quase esse período de tempo, se o serviço estiver travado e não aceitar conexões quando o Nginx tentar acessá-lo, mas antes que o Nginx possa exceder o tempo limite - é liberado e inicia o processamento, mas é muito lento e o Nginx seria interrompido devido ao tempo limite da leitura.

O tempo limite de conexão significa que o TCP para quando o handshake (por exemplo, não havia SYN_ACKs). O TCP tentaria novamente enviar SYNs, mas você deu apenas 2 segundos. para o Nginx usar outro servidor, portanto, simplesmente não há tempo para reenviar SYNs.

UPD. : Não foi possível encontrar nos documentos, mas o tcpdump mostra que há 3 segundos. atraso entre o 1º SYN enviado e a 2ª tentativa de enviar SYN.

poige
fonte
Eu não acho que isso é exatamente o que estou perguntando - a pergunta é: se o upstream travar e retornar o SYN_ACK após 1.999 segundos, por que o nginx não continuará com o processo com o upstream atual?
Guss
Bem, você pode usar o sniffer se quiser ter certeza exata. Pode acontecer que não haja ACKs em menos de 2 segundos.
poige
Eu realmente não posso usar um sniffer porque espero ver esse comportamento acontecer onde houver uma carga alta no sistema. A explicação de nunca há ACKs depois de alguns X, mas antes de 2 segundos, mesmo considerando milhões de solicitações, parece não plausível.
Guss