Nginx proxy_read_timeout vs. proxy_connect_timeout

15

Ich habe angefangen, Nginx als Reverse-Proxy für eine Reihe von Servern zu verwenden, die irgendeine Art von Dienst bereitstellen.

Der Dienst kann manchmal ziemlich langsam sein (er läuft auf Java und die JVM bleibt manchmal in der "vollständigen Garbage Collection" hängen, die mehrere Sekunden dauern kann), daher habe ich den proxy_connect_timeoutWert auf 2 Sekunden gesetzt, damit Nginx genügend Zeit zum Ermitteln hat Stellen Sie sicher, dass der Dienst auf dem GC nicht mehr reagiert und die Anforderung nicht rechtzeitig an einen anderen Server weitergeleitet wird.

Ich habe auch festgelegt proxy_read_timeout, dass der Reverse-Proxy nicht mehr reagiert, wenn der Dienst selbst zu viel Zeit für die Berechnung der Antwort benötigt. Auch hier sollte die Anforderung auf einen anderen Server verschoben werden, der frei genug sein sollte, um eine zeitnahe Antwort zurückzusenden.

Ich habe einige Benchmarks ausgeführt und kann deutlich sehen, dass das proxy_connect_timeoutordnungsgemäß funktioniert, da einige Anforderungen genau zu der für das Verbindungszeitlimit angegebenen Zeit zurückkehren, da der Dienst feststeckt und eingehende Verbindungen nicht akzeptiert (der Dienst verwendet Jetty als eingebettetes Medium) Servlet-Container). Das proxy_read_timeoutfunktioniert auch, da ich Anfragen sehen kann, die nach dem dort angegebenen Timeout zurückkehren.

Das Problem ist, dass ich erwartet hätte, dass einige Anfragen nach dieser Zeitüberschreitung proxy_read_timeout + proxy_connect_timeoutoder fast nach dieser Zeitspanne eingehen , wenn der Dienst nicht mehr funktioniert und keine Verbindungen akzeptiert, wenn Nginx versucht, darauf zuzugreifen, aber bevor Nginx eine Zeitüberschreitung ausführen kann - es wird freigegeben und startet die Verarbeitung, ist aber zu langsam und Nginx würde aufgrund des Zeitlimits für das Lesen abgebrochen. Ich glaube, dass der Service solche Fälle hat, aber nachdem ich mehrere Benchmarks mit insgesamt mehreren Millionen Anfragen ausgeführt habe, konnte ich keine einzige Anfrage sehen, die in irgendetwas weiter oben zurückkehrt proxy_read_timeout(was das größere Timeout ist).

Ich würde mich über jeden Kommentar zu diesem Problem freuen, auch wenn ich denke, dass dies an einem Fehler in Nginx liegen könnte (ich muss mir den Code erst noch ansehen, dies ist nur eine Annahme), dass der Timeout-Zähler nach dem Herstellen der Verbindung nicht zurückgesetzt wird ist erfolgreich, wenn Nginx nichts vom Upstream-Server gelesen hat.

Guss
quelle
1
Welche Version von NGINX? Ich denke, ich erinnere mich an etwas Ähnliches in einer älteren Version (etwa 0,6 / 7 vielleicht), aber es wurde in einer neueren Version behoben (Die neueste stabile Version ist 1.0.5), aber das könnte falsch sein. Immer noch zu wissen, dass Ihre Version helfen würde
Smudge
Beachten Sie, dass die Dokumentation proxy_read_timeoutnicht das "globale Zeitlimit" angibt, sondern zwischen zwei Lesevorgängen.
Poige
@Sam: Ich benutze Nginx 1.0.0. @poige - ja, mir ist das bewusst, deshalb erwarte ich eine totale Auszeit proxy_read_timeout + proxy_connect_timeout.
Guss
1
Als Randnotiz sollten Sie sich wahrscheinlich mit der gleichzeitigen Optimierung der Garbage Collection für Ihre JVM befassen
Polynom
@polynomial: wir haben aber die gleichzeitige Garbage Collection - Funktion führt zu mehr CPU - Zeit zu GC verloren insgesamt im Vergleich zu „Stopp der Welt“ GC, daher lieber investieren wir in Nginx Tuning nach unserer Benchmarks :-)
Guss

Antworten:

18

Ich konnte dies tatsächlich nicht reproduzieren auf:

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

Ich habe dies in meiner nginx.conf eingerichtet:

proxy_connect_timeout   10;
proxy_send_timeout      15;
proxy_read_timeout      20;

Ich habe dann zwei Testserver eingerichtet. Eine, die nur eine Zeitüberschreitung bei der SYN verursacht und eine, die Verbindungen akzeptiert, aber niemals antwortet:

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
}

Dann habe ich eine Testverbindung eingeschickt:

[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

Dann schaute error_log, das dies zeigte:

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"

dann:

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"

Und dann das access.log mit dem erwarteten Timeout von 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 -

Hier ist das von mir verwendete Protokollformat, das die einzelnen Upstream-Timeouts enthält:

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';
Polynom
quelle
1
Meine Frage oben in Ihrem Szenario ist eher wie folgt: Angenommen, ein Testserver akzeptiert die Verbindung nach einer zufälligen Zeit zwischen 0 und 20 Sekunden und wartet dann nach einer zufälligen Zeit zwischen 19 Sekunden und 21 Sekunden, bevor er antwortet. Führen Sie dann einen einfachen Benchmark durch. Ich würde erwarten, dass ungefähr 50% des Anforderungsergebnisses mit einem Timeout von 10 Sekunden, 25% mit einem Timeout von 20 bis 30 Sekunden und 25% eine erfolgreiche Antwort erhalten. Wie viele erfolgreiche Anfragen werden in einem solchen Fall mehr als 20 Sekunden dauern? In meinem Benchmark ist keiner von ihnen - und das stört mich.
Guss
Ich habe getestet, indem ich einen zufälligen Verlust für das SYN eingerichtet habe und dann ein CGI habe, das die Zeilen ungefähr 50 Sekunden lang wirklich langsam ausspuckt. Ich konnte feststellen, dass Anfragen länger dauerten als beide Timeouts zusammen, aber dennoch erfolgreich waren: box.access.log 200: 69.814: 67.100:.: 1579 33 127.0.0.1 test.host - [21 / Aug / 2011: 20: 30:52 -0700] "GET / huugs HTTP / 1.1" - "-" - "-" dev_edge 127.0.0.1:2280 -
Polynom
Ok, das ist auf einer ganz anderen Ebene komisch :-). Eine mögliche Erklärung ist, dass Nginx einige Zeit benötigt, um die Anfrage ( proxy_send_timeout) zu schreiben, und da Sie sie höher als festgelegt proxy_connection_timeouthaben, kann dies tatsächlich eine Verzögerung über die 20 Sekunden erklären proxy_read_timeout. Wenn du sagst "spucke Linien wirklich langsam aus" - was meinst du damit?
Guss
schlafe 1 zwischen dem Drucken von HTML-Zeilen im Hauptteil der Antwort. Nur das Anzeigen, wie das proxy_read_timeout zwischen den Lesevorgängen ist, nicht der gesamte Lesevorgang.
Polynom
1
Ah ich sehe. Nun, das ist definitiv nicht mein Fall und es tut mir leid, dass ich es in meinem OP nicht klargestellt habe. In meinem Fall schließt der Anwendungsserver die gesamte Verarbeitung ab, bevor eine Antwort zurückgegeben wird, und gibt dann alles auf einmal zurück - sodass proxy_read_timeoutdie Anforderung entweder vollständig fehlschlägt oder vollständig zugelassen wird. Dies erklärt auch den Unterschied zwischen dem Verhalten, das Sie sehen, und dem Verhalten, das ich sehe.
Guss
3

Das Problem ist, dass ich erwartet hätte, dass einige Anfragen nach proxy_read_timeout + proxy_connect_timeout abgelaufen sind, oder fast so lange, wenn der Dienst blockiert ist und keine Verbindungen akzeptiert, wenn Nginx versucht, darauf zuzugreifen, aber bevor Nginx das Timeout zulässt. es wird freigegeben und startet die Verarbeitung, ist aber zu langsam und Nginx würde aufgrund des Zeitlimits für das Lesen abgebrochen.

Das Zeitlimit für die Verbindung bedeutet, dass TCP beim Handshake blockiert (z. B. gab es keine SYN_ACKs). TCP würde erneut versuchen, SYNs zu senden, aber Sie haben nur 2 Sekunden gegeben. Nginx zu gehen, einen anderen Server zu verwenden, so hat es einfach keine Zeit zum erneuten Senden von SYNs.

UPD. : Konnte nicht in Dokumenten gefunden werden, aber tcpdump zeigt, dass es 3 Sek. Gibt. Verzögerung zwischen dem ersten SYN-Sendeversuch und dem zweiten SYN-Sendeversuch.

Poige
quelle
Ich glaube nicht, dass dies genau das ist, was ich frage - die Frage ist: Wenn der Upstream hängen bleibt und nach 1.999 Sekunden den SYN_ACK zurückgibt, warum setzt nginx den Prozess mit dem aktuellen Upstream nicht fort?
Guss
Nun, Sie können Sniffer verwenden, wenn Sie genau sicher sein wollen. Es könnte sich herausstellen, dass in <2 Sek. Überhaupt keine ACKs vorhanden sind.
Poige
Ich kann einen Sniffer nicht wirklich verwenden, da ich erwarte, dass dieses Verhalten bei einer hohen Systemlast auftritt. Die Erklärung, dass es nie ACKs gibt, die später als einige X, aber früher als 2 Sekunden sind, scheint nicht plausibel, selbst wenn Millionen von Anfragen berücksichtigt werden.
Guss