Nginx proxy_read_timeout vs. proxy_connect_timeout

12

Eu comecei a usar o Nginx como um 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 em Java e a JVM às vezes fica presa na "coleta de lixo completa" que pode levar vários segundos), então eu configurei o proxy_connect_timeout para 2 segundos, o que dê tempo suficiente para Nginx descobrir que o serviço está preso no GC e não responderá a tempo, e deve passar o pedido para um servidor diferente.

Eu também defini proxy_read_timeout para evitar que o proxy reverso fique travado se o próprio serviço levar muito tempo para calcular a resposta - novamente, ele deve mover a solicitação para outro servidor que deve ser livre o suficiente para retornar um resposta oportuna.

Eu executei alguns benchmarks e vejo claramente que o proxy_connect_timeout funciona corretamente, pois algumas solicitações retornam exatamente no tempo especificado para o tempo limite da conexão, pois o serviço está emperrado e não aceita conexões de entrada (o serviço está usando o Jetty como um contêiner de servlet incorporado). O proxy_read_timeout também funciona, pois vejo solicitações retornadas após o tempo limite especificado.

O problema é que eu esperaria ver alguns pedidos que excedem o tempo em proxy_read_timeout + proxy_connect_timeout , ou quase esse período de tempo, se o serviço estiver emperrado e não aceitará conexões quando o Nginx tentar acessá-lo, mas antes do Nginx pode timeout - ele é liberado e inicia o processamento, mas é muito lento e o Nginx seria interrompido devido ao tempo limite de leitura. Acredito que o serviço tenha tais casos, mas depois de executar vários benchmarks, totalizando vários milhões de pedidos - não consegui ver uma única solicitação que retorna em algo acima de proxy_read_timeout (que é o maior tempo limite).

Eu gostaria de receber algum comentário sobre este assunto, embora eu ache que isso pode ser devido a um bug no Nginx (eu ainda não vi o código, então isso é apenas uma suposição) que o contador de timeout não é resetado depois que a conexão for bem-sucedida, se o Nginx não tiver lido nada do servidor upstream.

    
por Guss 05.07.2011 / 15:14

2 respostas

17

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, configuro dois servidores de teste. Um que só iria timeout no SYN, e um 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
}

Em seguida, 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

Em seguida, assisti ao 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, em seguida, 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 registro 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';
    
por 21.08.2011 / 05:18
3

The problem is that I would have expected to see some requests that timeout after proxy_read_timeout + proxy_connect_timeout, or almost that length of time, if the service is stuck and won't accept connections when Nginx tries to access it, but before Nginx can timeout - it gets released and starts processing, but is too slow and Nginx would abort because of the read timeout.

Conectar o tempo limite significa que o TCP trava quando o handshaking (por exemplo, não houve SYN_ACKs). O TCP iria tentar enviar novamente os SYNs, mas você deu apenas 2 segundos. ao Nginx para ir usar outro servidor, então ele simplesmente não tem tempo para re-enviar SYNs.

UPD. : não foi encontrado em documentos, mas o tcpdump mostra que há 3 seg. atraso entre o primeiro enviado SYN e a segunda tentativa de enviar SYN.

    
por 20.08.2011 / 04:09