Nginx proxy_read_timeout vs. proxy_connect_timeout


15

Ho iniziato a utilizzare Nginx come proxy inverso per un set di server che forniscono una sorta di servizio.

Il servizio può essere piuttosto lento a volte (il suo funzionamento su Java e JVM a volte si blocca nella "raccolta completa dei rifiuti" che potrebbe richiedere diversi secondi), quindi ho impostato i proxy_connect_timeout2 secondi, il che darà a Nginx il tempo sufficiente per capire che il servizio è bloccato su GC e non risponderà in tempo, e dovrebbe passare la richiesta a un altro server.

Ho anche impostato proxy_read_timeoutper impedire che il proxy inverso si blocchi se il servizio stesso impiega troppo tempo per calcolare la risposta - di nuovo, dovrebbe spostare la richiesta su un altro server che dovrebbe essere abbastanza libero da restituire una risposta tempestiva.

Ho eseguito alcuni benchmark e vedo chiaramente che proxy_connect_timeoutfunziona correttamente poiché alcune richieste ritornano esattamente nel tempo specificato per il timeout della connessione, poiché il servizio è bloccato e non accetta connessioni in entrata (il servizio utilizza Jetty come incorporato contenitore servlet). Il proxy_read_timeoutfunziona anche, come posso vedere le richieste che il ritorno dopo il timeout non specificato.

Il problema è che mi sarei aspettato di vedere alcune richieste di timeout dopo proxy_read_timeout + proxy_connect_timeout, o quasi per quel periodo di tempo, se il servizio è bloccato e non accetta connessioni quando Nginx tenta di accedervi, ma prima che Nginx possa scadere - viene rilasciato e inizia l'elaborazione, ma è troppo lento e Nginx si interromperà a causa del timeout di lettura. Credo che il servizio abbia casi del genere, ma dopo aver eseguito diversi benchmark, per un totale di diversi milioni di richieste, non sono riuscito a vedere una singola richiesta che ritorna in qualsiasi cosa sopra proxy_read_timeout(che è il timeout più grande).

Gradirei qualsiasi commento su questo problema, anche se penso che potrebbe essere dovuto a un bug in Nginx (devo ancora guardare il codice, quindi questo è solo un presupposto) che il contatore del timeout non viene ripristinato dopo la connessione ha esito positivo, se Nginx non ha letto nulla dal server upstream.


1
Quale versione di NGINX? Penso di ricordare qualcosa di simile in una versione precedente (circa 0.6 / 7 forse) ma è stato risolto in una versione più recente (l'ultima versione stabile è 1.0.5), ma potrebbe essere sbagliato. Sapendo ancora che la tua versione sarebbe d'aiuto
Sfumino il

Si noti che i documenti dicono che proxy_read_timeoutnon è il "timeout globale", ma tra 2 operazioni di lettura.
poige

@ Sam: sto usando Nginx 1.0.0. @poige - sì, ne sono consapevole, motivo per cui mi aspetto che il timeout totale sia proxy_read_timeout + proxy_connect_timeout.
Guss,

1
Come nota a margine, dovresti probabilmente ricercare alcune sintonizzazioni simultanee di garbage collection per la tua JVM: en.wikipedia.org/wiki/…
polinomio

@polynomial: lo abbiamo fatto, ma in base ai nostri benchmark, la funzione di garbage collection simultanea comporta una maggiore perdita di tempo della CPU rispetto al GC in generale rispetto al GC "stop the world", quindi preferiamo investire nell'ottimizzazione Nginx :-)
Guss,

Risposte:


18

In realtà non sono stato in grado di riprodurre questo su:

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

Ho impostato questo nel mio nginx.conf:

proxy_connect_timeout   10;
proxy_send_timeout      15;
proxy_read_timeout      20;

Ho quindi installato due server di prova. Uno che si limiterebbe a timeout su SYN e uno che accetterebbe connessioni ma non risponderebbe mai:

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
}

Quindi ho inviato una connessione di prova:

[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

Quindi guardato error_log che mostrava questo:

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"

poi:

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 poi access.log che ha il timeout previsto per 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 -

Ecco il formato di registro che sto usando che include i singoli timeout upstream:

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';

1
La mia domanda sopra, nel tuo scenario, è più simile a questa: supponi un server di prova che accetta la connessione dopo un tempo casuale tra 0 e 20 secondi, quindi attende un tempo casuale tra 19 secondi e 21 secondi prima di rispondere. Quindi eseguire un semplice benchmark contro di esso. Mi aspetto di vedere circa il 50% delle richieste con un timeout di 10 secondi, il 25% con un timeout di 20 ~ 30 secondi e il 25% riceverà una risposta corretta. In tal caso, quante richieste riuscite impiegheranno più di 20 secondi per essere completate? Nel mio benchmark, nessuno di loro lo è - e questo mi preoccupa.
Guss,

Ho provato impostando una perdita casuale sul SYN e poi avendo un CGI che sputava le linee molto lentamente per circa 50 secondi. Sono stato in grado di vedere le richieste impiegare molto più tempo di entrambi i timeout combinati ma continuando ad avere successo: 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 -
polinomiale

Ok, è strano a un livello completamente diverso :-). Una possibile spiegazione è che Nginx impiega del tempo a scrivere la richiesta ( proxy_send_timeout) e, dato che lo hai impostato su un valore più alto proxy_connection_timeout, può effettivamente tenere conto di eventuali ritardi nell'arco dei 20 secondi proxy_read_timeout. Quando dici "sputa le linee molto lentamente" - cosa intendi?
Guss,

sleep 1 tra le righe di stampa HTML nel corpo della risposta. Esporre semplicemente come proxy_read_timeout è tra le letture e non l'intera lettura.
polinomio

1
Ah, capisco. Bene, questo non è assolutamente il mio caso e mi dispiace di non averlo chiarito nel mio PO. Nel mio caso, il server delle applicazioni completa l'intera elaborazione prima di restituire qualsiasi tipo di risposta, quindi restituisce tutto in una volta, quindi proxy_read_timeoutla richiesta non riesce completamente o la consente completamente. Questo spiega anche la differenza tra il comportamento che vedi e il comportamento che vedo.
Guss,

3

Il problema è che mi sarei aspettato di vedere alcune richieste che scadevano dopo proxy_read_timeout + proxy_connect_timeout, o quasi per quel periodo di tempo, se il servizio è bloccato e non accetta connessioni quando Nginx tenta di accedervi, ma prima che Nginx possa scadere - viene rilasciato e inizia l'elaborazione, ma è troppo lento e Nginx si interromperà a causa del timeout di lettura.

Il timeout di connessione indica che TCP si blocca durante l'handshaking (ad esempio, non c'erano SYN_ACK). TCP riproverebbe a inviare SYN, ma hai dato solo per 2 secondi. su Nginx per utilizzare un altro server, quindi semplicemente non ha tempo per inviare nuovamente i SYN.

UPD. : Impossibile trovare nei documenti, ma tcpdump mostra che ci sono 3 secondi. ritardo tra il 1 ° SYN inviato e il 2 ° tentativo di invio SYN.


Non penso che sia esattamente quello che sto ponendo: la domanda è: se l'upstream si blocca e restituisce SYN_ACK dopo 1.999 secondi, perché nginx non continua con il processo con l'upstream corrente?
Guss,

Bene, puoi usare lo sniffer se vuoi essere sicuro esattamente. Si potrebbe scoprire che non ci sono ACK in <2 secondi.
poige

Non riesco davvero a usare uno sniffer perché mi aspetto che questo comportamento si verifichi in presenza di un carico elevato sul sistema. La spiegazione di non ci sono mai ACK dopo qualche X ma prima di 2 secondi, anche se si considerano milioni di richieste, sembra non plausibile.
Guss,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.