Nginx proxy_read_timeout contre proxy_connect_timeout

15

J'ai commencé à utiliser Nginx comme proxy inverse pour un ensemble de serveurs qui fournissent une sorte de service.

Le service peut parfois être assez lent (son fonctionnement sur Java et la JVM est parfois coincé dans une "récupération de place complète" qui peut prendre plusieurs secondes), j'ai donc défini la valeur proxy_connect_timeoutà 2 secondes, ce qui donnera à Nginx suffisamment de temps pour comprendre que le service est bloqué sur GC et ne répondra pas à temps, et il doit transmettre la demande à un autre serveur.

J'ai également décidé proxy_read_timeoutd'empêcher le proxy inverse de se bloquer si le service lui-même prend trop de temps pour calculer la réponse - encore une fois, il devrait déplacer la demande vers un autre serveur qui devrait être suffisamment libre pour renvoyer une réponse en temps opportun.

J'ai exécuté des tests de performance et je peux voir clairement que cela proxy_connect_timeoutfonctionne correctement car certaines demandes retournent exactement à l'heure spécifiée pour le délai de connexion, car le service est bloqué et n'accepte pas les connexions entrantes (le service utilise Jetty comme un embarqué conteneur de servlet). Le proxy_read_timeoutfonctionne également, comme je peux voir que les demandes de retour après le délai spécifié il.

Le problème est que je m'attendais à voir certaines demandes qui expirent après proxy_read_timeout + proxy_connect_timeout, ou presque cette durée, si le service est bloqué et n'accepte pas les connexions lorsque Nginx essaie d'y accéder, mais avant que Nginx ne puisse expirer - il est libéré et commence le traitement, mais est trop lent et Nginx serait abandonné en raison du délai de lecture. Je crois que le service a de tels cas, mais après avoir exécuté plusieurs tests de référence, totalisant plusieurs millions de demandes - je n'ai pas vu une seule demande qui retourne dans quoi que ce soit ci-dessus proxy_read_timeout(qui est le plus grand délai d'attente).

J'apprécierais tout commentaire sur ce problème, bien que je pense que cela pourrait être dû à un bogue dans Nginx (je n'ai pas encore examiné le code, donc ce n'est qu'une hypothèse) que le compteur de délai d'attente n'est pas réinitialisé après la connexion est réussie, si Nginx n'a rien lu du serveur en amont.

Guss
la source
1
Quelle version de NGINX? Je pense que je me souviens de quelque chose de similaire dans une ancienne version (environ 0.6 / 7 peut-être) mais il a été corrigé dans une version plus récente (la dernière version stable est 1.0.5), mais cela pourrait être faux. Savoir que votre version aiderait
Smudge
Notez que les documents disent que ce proxy_read_timeoutn'est pas le «délai global», mais entre 2 opérations de lecture.
poige
@Sam: J'utilise Nginx 1.0.0. @poige - oui, je suis conscient de cela, c'est pourquoi je m'attends à ce que le délai total soit proxy_read_timeout + proxy_connect_timeout.
Guss
1
En guise de remarque, vous devriez probablement rechercher des réglages de récupération de place simultanés pour votre machine virtuelle Java
polynôme
@polynomial: nous l'avons fait, mais selon nos critères de référence, la fonction de récupération de place simultanée entraîne une perte de temps CPU globale pour GC par rapport à GC "stop the world", nous préférons donc investir dans le réglage Nginx :-)
Guss

Réponses:

18

Je n'ai en fait pas pu reproduire ceci sur:

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

J'ai mis cela en place dans mon nginx.conf:

proxy_connect_timeout   10;
proxy_send_timeout      15;
proxy_read_timeout      20;

J'ai ensuite installé deux serveurs de test. Un qui expirerait simplement sur le SYN et un qui accepterait les connexions mais ne répondrait jamais:

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
}

Ensuite, j'ai envoyé une connexion de test:

[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

Puis regardé error_log qui a montré ceci:

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"

ensuite:

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"

Et puis le fichier access.log qui a le délai d'attente 30s prévu (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 -

Voici le format de journal que j'utilise, qui inclut les délais d'attente individuels en amont:

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';
polynôme
la source
1
Ma question ci-dessus, dans votre scénario, ressemble plus à ceci: supposez un serveur de test qui accepte la connexion après un temps aléatoire entre 0 et 20 secondes, puis attend un temps aléatoire entre 19 secondes et 21 secondes avant de répondre. Ensuite, exécutez un benchmark simple contre lui. Je m'attendrais à voir environ 50% du résultat des demandes avec un délai de 10 secondes, 25% avec un délai de 20 à 30 secondes et 25% recevront une réponse réussie. Dans un tel cas, combien de demandes réussies prendront plus de 20 secondes pour être traitées? Dans mon indice de référence, aucun d'entre eux ne l'est - et cela me trouble.
Guss
J'ai testé en configurant une perte aléatoire sur le SYN et en ayant ensuite un CGI qui crache des lignes très lentement pendant environ 50 secondes. J'ai pu voir que les demandes prenaient beaucoup plus de temps que les deux délais d'expiration combinés mais étaient toujours couronnées de succès: box.access.log 200: 69.814: 67.100:.: 1579 33 127.0.0.1 test.host - [21 / août / 2011: 20: 30:52 -0700] "GET / huugs HTTP / 1.1" "-" "-" "-" dev_edge 127.0.0.1:2280 -
polynôme
Ok, c'est bizarre à un tout autre niveau :-). Une explication possible est qu'il faut du temps à Nginx pour écrire la requête ( proxy_send_timeout) et comme vous l'avez réglée à un niveau supérieur proxy_connection_timeout, cela peut en fait expliquer tout retard au cours des 20 secondes proxy_read_timeout. Lorsque vous dites "cracher des lignes très lentement" - que voulez-vous dire?
Guss
dormir 1 entre les lignes d'impression de HTML dans le corps de la réponse. Juste exposer comment le proxy_read_timeout se situe entre les lectures et non la lecture entière.
polynôme
1
Ah, je vois. Eh bien, ce n'est certainement pas mon cas et je suis désolé de ne pas l'avoir précisé dans mon OP. Dans mon cas, le serveur d'applications termine l'intégralité du traitement avant de renvoyer tout type de réponse, puis renvoie tout à la fois - de sorte proxy_read_timeoutque la demande échoue complètement ou l'autorise complètement. Cela explique également la différence entre le comportement que vous voyez et le comportement que je vois.
Guss
3

Le problème est que je m'attendais à voir certaines demandes expirer après proxy_read_timeout + proxy_connect_timeout, ou presque cette durée, si le service est bloqué et n'accepte pas les connexions lorsque Nginx essaie d'y accéder, mais avant que Nginx ne puisse expirer - il est libéré et commence le traitement, mais est trop lent et Nginx abandonnerait en raison du délai de lecture.

Le délai d'expiration de la connexion signifie que TCP se bloque lors de l'établissement d'une liaison (par exemple, il n'y avait pas de SYN_ACK). TCP essaierait à nouveau d'envoyer des SYN, mais vous n'avez donné que 2 secondes. à Nginx pour utiliser un autre serveur, il n'a donc tout simplement pas le temps de renvoyer des SYN.

UPD. : Impossible de trouver dans les documents, mais tcpdump montre qu'il y a 3 secondes. délai entre le 1er SYN envoyé et la 2e tentative d'envoi du SYN.

poige
la source
Je ne pense pas que c'est exactement ce que je demande - la question est: si l'amont est bloqué et renvoie le SYN_ACK après 1,999 secondes, pourquoi nginx ne poursuivra-t-il pas le processus avec l'amont actuel?
Guss
Eh bien, vous pouvez utiliser le renifleur si vous voulez en être sûr. Il peut s'avérer qu'il n'y a aucun ACK en moins de 2 secondes.
poige
Je ne peux pas vraiment utiliser un renifleur car je m'attends à voir ce comportement se produire là où il y a une charge élevée sur le système. L'explication qu'il n'y a jamais d'accusé de réception plus tard que certains X mais plus tôt que 2 secondes, même si l'on considère des millions de demandes, semble non plausible.
Guss