Nginx proxy_read_timeout vs. proxy_connect_timeout

15

Comencé a usar Nginx como proxy inverso para un conjunto de servidores que proporcionan algún tipo de servicio.

El servicio puede ser bastante lento a veces (se está ejecutando en Java y la JVM a veces se atasca en una "recolección de basura completa" que puede tomar varios segundos), así que configuré proxy_connect_timeout2 segundos, lo que le dará a Nginx suficiente tiempo para calcular El servicio está atascado en el GC y no responderá a tiempo, y debe pasar la solicitud a un servidor diferente.

También configuré proxy_read_timeoutpara evitar que el proxy inverso se atasque si el servicio en sí toma demasiado tiempo para calcular la respuesta; nuevamente, debe mover la solicitud a otro servidor que debería ser lo suficientemente libre como para devolver una respuesta oportuna.

He ejecutado algunos puntos de referencia y puedo ver claramente que proxy_connect_timeoutfunciona correctamente, ya que algunas solicitudes regresan exactamente en el tiempo especificado para el tiempo de espera de la conexión, ya que el servicio está atascado y no acepta conexiones entrantes (el servicio está utilizando Jetty como incrustado servlet contenedor). El proxy_read_timeouttambién funciona, ya que puedo ver las solicitudes que regresan después del tiempo de espera especificado allí.

El problema es que habría esperado ver algunas solicitudes que expiran después de ese tiempo proxy_read_timeout + proxy_connect_timeout, o casi ese período de tiempo, si el servicio está atascado y no acepta conexiones cuando Nginx intenta acceder, pero antes de que Nginx pueda expirar, se libera e inicia el procesamiento, pero es demasiado lento y Nginx abortaría debido al tiempo de espera de lectura. Creo que el servicio tiene tales casos, pero después de ejecutar varios puntos de referencia, totalizando varios millones de solicitudes, no pude ver una sola solicitud que regrese en algo anterior proxy_read_timeout(que es el tiempo de espera más grande).

Agradecería cualquier comentario sobre este problema, aunque creo que podría deberse a un error en Nginx (aún no he visto el código, por lo que esto es solo una suposición) de que el contador de tiempo de espera no se restablece después de la conexión es exitoso si Nginx no leyó nada del servidor ascendente.

Guss
fuente
1
¿Qué versión de NGINX? Creo que recuerdo algo similar en una versión anterior (aproximadamente 0.6 / 7 tal vez) pero se solucionó en una versión más reciente (la última versión estable es 1.0.5), pero eso podría estar mal. Aún sabiendo que su versión ayudaría
Smudge
Tenga en cuenta que los documentos dicen proxy_read_timeoutque no es el "tiempo de espera global", sino entre 2 operaciones de lectura.
Poige
@Sam: estoy usando Nginx 1.0.0. @poige: sí, soy consciente de eso, por eso espero que sea el tiempo de espera total proxy_read_timeout + proxy_connect_timeout.
Guss
1
Como nota al margen, probablemente debería investigar algunos ajustes simultáneos de recolección de basura para su JVM: en.wikipedia.org/wiki/…
polinomio
@polynomial: lo hicimos, pero de acuerdo con nuestros puntos de referencia, la función de recolección de basura concurrente resulta en más tiempo de CPU perdido para GC en general en comparación con GC "detener el mundo", por lo tanto, preferimos invertir en el ajuste de Nginx :-)
Guss

Respuestas:

18

En realidad no pude reproducir esto en:

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

Configuré esto en mi nginx.conf:

proxy_connect_timeout   10;
proxy_send_timeout      15;
proxy_read_timeout      20;

Luego configuro dos servidores de prueba. Uno que simplemente agotaría el tiempo de espera en el SYN, y uno que aceptaría conexiones pero nunca respondería:

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
}

Luego envié una conexión de prueba:

[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

Luego vi error_log que mostró esto:

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"

luego:

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"

Y luego el acceso.log que tiene el tiempo de espera esperado de 30 años (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 -

Aquí está el formato de registro que estoy usando, que incluye los tiempos de espera ascendentes individuales:

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';
polinomio
fuente
1
Mi pregunta anterior, en su escenario, es más como esta: suponga que un servidor de prueba acepta la conexión después de un tiempo aleatorio entre 0 y 20 segundos, y luego espera un tiempo aleatorio entre 19 segundos y 21 segundos antes de responder. Luego ejecute un punto de referencia simple contra él. Esperaría ver aproximadamente el 50% del resultado de las solicitudes con un tiempo de espera de 10 segundos, el resultado del 25% con un tiempo de espera de 20 ~ 30 segundos y el 25% recibirá una respuesta exitosa. En tal caso, ¿cuántas solicitudes exitosas tardarán más de 20 segundos en completarse? En mi punto de referencia, ninguno de ellos lo es, y eso me preocupa.
Guss
Probé configurando la pérdida aleatoria en el SYN y luego teniendo un CGI que escupía líneas muy lentamente durante unos 50 segundos. Pude ver que las solicitudes tardaban mucho más que los dos tiempos de espera combinados, pero aún así tenían éxito: 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 -
polinomio
Ok, eso es extraño en un nivel completamente diferente :-). Una posible explicación es que a Nginx le lleva tiempo escribir la solicitud ( proxy_send_timeout) y, como la ha configurado en un nivel superior proxy_connection_timeout, eso puede explicar cualquier retraso en los 20 segundos proxy_read_timeout. Cuando dices "escupir líneas muy lentamente", ¿qué quieres decir?
Guss
dormir 1 entre líneas de impresión de HTML en el cuerpo de la respuesta. Simplemente exponiendo cómo proxy_read_timeout es entre lecturas, no la lectura completa.
polinomio
1
Ah, ya veo. Bueno, definitivamente este no es mi caso y lamento no haberlo dejado claro en mi OP. En mi caso, el servidor de aplicaciones completa todo el procesamiento antes de devolver cualquier tipo de respuesta, y luego devuelve todo de una vez, por lo proxy_read_timeoutque falla la solicitud por completo o lo permite por completo. Esto también explica la diferencia entre el comportamiento que ves y el comportamiento que yo veo.
Guss
3

El problema es que habría esperado ver algunas solicitudes que expiran después de proxy_read_timeout + proxy_connect_timeout, o casi ese período de tiempo, si el servicio está atascado y no acepta conexiones cuando Nginx intenta acceder a él, pero antes de que Nginx pueda expirar - se libera y comienza a procesarse, pero es demasiado lento y Nginx abortaría debido al tiempo de espera de lectura.

El tiempo de espera de conexión significa que el TCP se detiene cuando se establece el protocolo de enlace (por ejemplo, no hubo SYN_ACKs). TCP volvería a intentar enviar SYN, pero solo ha dado 2 segundos. a Nginx para usar otro servidor, por lo que simplemente no tiene tiempo para volver a enviar SYN.

UPD : No se pudo encontrar en los documentos, pero tcpdump muestra que hay 3 segundos. retraso entre el primer SYN enviado y el segundo intento de enviar SYN.

poige
fuente
No creo que esto sea exactamente lo que estoy preguntando: la pregunta es: si el flujo ascendente se atasca y devuelve el SYN_ACK después de 1.999 segundos, ¿por qué nginx no continuará con el proceso con el flujo ascendente actual?
Guss
Bueno, puedes usar sniffer si quieres estar seguro exactamente. Podría resultar que no hay ACK en <2 segundos en absoluto.
Poige
Realmente no puedo usar un sniffer porque espero ver que este comportamiento suceda donde hay una carga alta en el sistema. La explicación de que nunca hay ACK más tarde que X, pero antes de 2 segundos, incluso cuando se consideran millones de solicitudes, parece no plausible.
Guss