12 votos

La petición POST se repite con el servidor nginx loadbalanced (estado 499)

Cargas dobles

Desde que pasamos de una simple instancia de Apache a un entorno con equilibrio de carga, a veces hay problemas con las peticiones POST que se repiten. Estamos ejecutando nginx como proxy inverso. El contenido estático es servido por el propio nginx, y el contenido dinámico es servido desde dos backends de Apache.

He comprobado que no se trata de un error de interfaz/usuario. Un pequeño ejemplo: una simple carga de imagen hará que la imagen se cargue dos veces. La petición/POST no se envía dos veces por doble click o fallo del usuario. No he encontrado ninguna evidencia de que el navegador esté enviando la petición dos veces, así que mi sospecha está en el lado del servidor. (Nótese que esto es sólo una sospecha.) La mayoría de estas peticiones son internas, es decir, provienen de empleados, por lo que puedo verificar cómo se producen.

La única cosa 'mal' que puedo encontrar es que nginx registrará un 499 error en estos casos. Sin embargo, no estoy seguro de si ésta es la causa o sólo un efecto (secundario) del problema. (Soy consciente de que 499 no es un estado http por defecto, es un estado de nginx que significa "el cliente ha cerrado la conexión")

solicita

Las peticiones POST repetidas son casi todas peticiones que pueden tardar un rato. La que muestro aquí como ejemplo es una simple subida de imagen, pero el script hace algunas cosas en segundo plano (la imagen debe ser convertida a diferentes formatos/tamaños, y debe ser distribuida a ambos servidores, etc).

Registros

Un ejemplo es la subida de una imagen. nginx registrará una petición '499' y otra '200', pero Apache está recibiendo (¡y gestionando!) dos peticiones.

Apache

[17:17:37 +0200] "POST ***URL** HTTP/1. 0" 200 9045   
[17:17:47 +0200] "POST ***URL** HTTP/1. 0" 200 20687

nginx

[17:17:47 +0200] "POST ***URL** HTTP/1.1" 499 0 
[17:17:52 +0200] "POST ***URL** HTTP/1.1" 200 5641

Sospechas

Me parece que las subidas más grandes/lentas sufren más de esto, así que sospecho de un tiempo de espera. He intentado leer sobre el error 499: las conclusiones parecen ser que se trata de "conexión cerrada por el cliente". Ese podría ser el caso en el fondo, pero no estoy seguro de cómo esto significaría que una segunda solicitud debe ser emitida y ciertamente no hay algo como "usuario cerró el navegador" pasando.

Actualmente parece ayudar a dividir las peticiones POST más lentas (si hay varias cosas que hacer, sólo hay que hacer que el usuario elija 1 y hacer POST una segunda vez para la otra), pero esto podría ser sólo disminuir la probabilidad de que ocurra. No estoy seguro.

Obviamente, se trata de una solución temporal. Si se es un tiempo de espera, tengo que averiguar dónde y aumentar los números correspondientes, pero no estoy seguro de por qué un tiempo de espera causaría este comportamiento:Yo sospecharía un mensaje de "bueno, eso salió mal", no una repetición.

Preguntas

Quiero averiguar qué proceso/situación puede hacer que se repita un POST. Por supuesto, cualquier "no estoy seguro de por qué, pero se solucionará aumentando este tiempo de espera" es genial también.

configuraciones de nginx

NGINX.conf

user  nginx;
worker_processes  2;
worker_rlimit_nofile 10240;

error_log  /var/log/nginx/error.log error;
pid        /var/run/nginx.pid;

events {
    multi_accept on;
    worker_connections  4096;
    use epoll;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nodelay     off;    
    client_max_body_size    30m;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
}

conf.d

He eliminado algunas líneas específicas de IP en el geo partes, así como el SSL variaciones, para simplificar. Si es necesario puedo reemplazarlos, pero se reduce a un extra geo para los backends ssl, y los correspondientes upstreams y archivos conf.

geo $backend {
    default apache-backend;
}

upstream apache-backend {
    ip_hash;
    server SERVER1 max_fails=3 fail_timeout=30s weight=2;
    server SERVER2 max_fails=3 fail_timeout=30s weight=3;
}

conf.d/somestring.conf

limit_conn_zone $binary_remote_addr zone=somestring:10m;
server {
    listen ip1:80;
    listen ip2:80;
    server_name name.tld www.name.tld;

    root PATH

    access_log PATH/name.log main;

    location / {
            proxy_pass              http://$backend;
    }

            //*some more locations**//

    gzip on;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_min_length 1100;
    gzip_buffers 16 8k;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript;
}

conf.d/proxy.conf

proxy_set_header        Accept-Encoding "";
proxy_set_header        X-Real-IP $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header        Host $http_host;

proxy_buffering         on;
proxy_read_timeout      90;
proxy_buffer_size       32k;
proxy_buffers           8 32k;
proxy_busy_buffers_size    32k;
proxy_temp_file_write_size 32k;

6voto

Yi Ling Puntos 53

Respuesta corta: pruebe esto para su bloque de ubicación:

location / {
  proxy_read_timeout 120;
  proxy_next_upstream error;
  proxy_pass http://$backend;
}

Una explicación más larga:

Creo que acabo de encontrarme exactamente con el problema que describes:

  • Yo uso nginx proxy inverso como un equilibrador de carga
  • para solicitudes de larga duración, el backend recibe la misma solicitud varias veces
  • los registros de acceso nginx de los nodos ascendentes muestran 499 para estas solicitudes, y la misma solicitud aparece en diferentes nodos ascendentes

Resulta que este es en realidad el comportamiento por defecto de nginx como proxy inverso, y actualizarlo a versiones superiores por lo tanto no resolverá este problema, aunque se dio como una posible solución aquí pero esto aborda una cuestión diferente.

Ocurre porque nginx como equilibrador de carga elige un nodo ascendente en moda round-robin . Cuando el nodo elegido falla, la petición se envía al siguiente nodo. Lo importante a tener en cuenta aquí es que el fallo de un nodo se clasifica por defecto como error or timeout . Dado que no ha establecido un proxy_read_timeout El valor predeterminado es 60 segundos. Así que después de 60 segundos de espera, nginx elige el siguiente nodo y envía la misma solicitud.

Así que una solución es aumentar este tiempo de espera para que su operación de larga duración puede completar, por ejemplo, mediante el establecimiento de proxy_read_timeout 120; (o cualquier límite que se adapte a tus necesidades).

Otra opción es evitar que el proxy inverso intente utilizar el siguiente nodo, en caso de timeout, configurando proxy_next_upstream error; . O puede configurar ambas opciones, como se sugiere más arriba.

1voto

Nanne Puntos 254

En este tema del foro nos enteramos de que la huella podría ser SPDY. Para ese usuario parece una solución desactivarlo, y tampoco hemos tenido dobles posts desde que lo desactivamos.

El problema exacto, aparte de "SPDY lo hizo", es desconocido en este momento, los efectos secundarios de la solución propuesta (desactivar SPDY) son, obviamente, "no más SPDY", pero podemos vivir con eso.

Hasta que el fallo no vuelva a aparecer, lo considero un "arreglo" (o al menos: una solución al problema).

edit: No hemos vuelto a ver (25-02-2014) este problema, así que parece una solución duradera.

EnMiMaquinaFunciona.com

EnMiMaquinaFunciona es una comunidad de administradores de sistemas en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros sysadmin, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X