12 votes

La demande POST est répétée avec le serveur nginx loadbalanced (statut 499)

Doubles téléchargements

Depuis que nous sommes passés d'une simple instance d'Apache à un environnement à répartition de charge, il y a parfois des problèmes de répétition des requêtes POST. Nous utilisons nginx comme proxy inverse. Le contenu statique est servi par nginx lui-même, et le contenu dynamique est servi par deux backends Apache.

J'ai vérifié qu'il ne s'agit pas d'une erreur d'interface/utilisateur. Un petit exemple : un simple chargement d'image entraîne un double chargement de l'image. La requête/POST n'est pas envoyée deux fois par un double clic ou une erreur de l'utilisateur. Je n'ai trouvé aucune preuve que le navigateur envoie la demande deux fois, je soupçonne donc le serveur. (Notez qu'il ne s'agit que de soupçons.) La plupart de ces demandes sont internes, c'est-à-dire qu'elles proviennent d'employés, et je peux donc vérifier comment elles se produisent.

La seule chose qui ne va pas, c'est que nginx enregistre un message de type 499 erreur dans ces cas. Je ne suis cependant pas sûr que ce soit la cause ou juste un effet (secondaire) du problème. (Je suis conscient que 499 n'est pas un statut http par défaut, c'est un statut nginx qui signifie "le client a fermé la connexion").

demande

Les requêtes POST répétées sont presque toutes des requêtes qui peuvent prendre un certain temps. Celle que je montre ici à titre d'exemple est un simple téléchargement d'image, mais le script fait quelques trucs en arrière-plan (l'image doit être convertie en différents formats/tailles, et doit être distribuée aux deux serveurs, etc).

journaux

Par exemple, le téléchargement d'une image. nginx enregistre une requête '499' et une requête 200, mais Apache reçoit (et traite !) deux requêtes.

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

Soupçons

Il me semble que les téléchargements les plus volumineux et les plus lents souffrent davantage de ce problème, je soupçonne donc un délai d'attente. J'ai essayé de me documenter sur l'erreur 499 : les conclusions semblent être qu'il s'agit d'une "connexion fermée par le client". Cela pourrait être le cas en arrière-plan, mais je ne suis pas sûr que cela signifie qu'une deuxième demande devrait être émise et il n'y a certainement pas quelque chose comme "l'utilisateur a fermé le navigateur".

Actuellement, il semble aider à briser les requêtes POST plus lentes (s'il y a plusieurs choses à faire, il suffit de faire en sorte que l'utilisateur en choisisse une et effectue un second POST pour l'autre), mais cela pourrait simplement réduire les chances que cela se produise. Je ne suis pas sûr.

Il s'agit évidemment d'une solution temporaire. Si elle est un délai d'attente, je dois trouver où et augmenter les nombres correspondants, mais je ne suis pas sûr de la raison pour laquelle un délai d'attente provoquerait ce comportement : je soupçonnerais un message " well, that went wrong ", pas une répétition.

Questions

Je cherche à savoir quel processus/situation peut entraîner la répétition d'un POST. Bien sûr, tout "je ne suis pas sûr de la raison, mais cela sera corrigé en augmentant ce délai" sera également apprécié.

configurations 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

J'ai supprimé certaines lignes spécifiques à l'IP dans le geo ainsi que les SSL variations, pour rester simple. Si nécessaire, je peux les remplacer, mais cela se résume à un supplément d'argent. geo pour les backends ssl, et les upstreams et fichiers conf correspondants.

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 Points 53

Réponse courte : essayez ceci pour votre bloc de localisation :

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

Une explication plus longue :

Je pense que je viens de rencontrer exactement le problème que vous avez décrit :

  • J'utilise le proxy inverse de nginx comme équilibreur de charge.
  • pour les demandes de longue durée, le backend reçoit la même demande plusieurs fois
  • les journaux d'accès nginx des noeuds en amont montrent 499 pour ces demandes, et la même demande apparaît dans différents nœuds en amont.

Il s'avère que c'est en fait le comportement par défaut de nginx en tant que proxy inverse, et la mise à niveau vers des versions supérieures ne résoudra donc pas ce problème, bien que cela ait été donné comme une solution possible ici mais cela concerne une autre question.

Cela se produit parce que nginx, en tant qu'équilibreur de charge, choisit un nœud en amont dans mode round-robin . Lorsque le nœud choisi échoue, la demande est envoyée au nœud suivant. Ce qu'il est important de noter ici, c'est que l'échec d'un nœud est, par défaut, classé comme error or timeout . Puisque vous n'avez pas défini un proxy_read_timeout La valeur par défaut est de 60 secondes. Ainsi, après 60 secondes d'attente, nginx choisit le nœud suivant et envoie la même requête.

Une solution est donc d'augmenter ce délai afin que votre opération de longue durée puisse se terminer, par exemple en définissant le paramètre proxy_read_timeout 120; (ou toute autre limite adaptée à vos besoins).

Une autre option consiste à empêcher le proxy inverse d'essayer d'utiliser le nœud suivant, en cas de dépassement du délai d'attente, en définissant l'option proxy_next_upstream error; . Ou vous pouvez définir ces deux options, comme suggéré ci-dessus.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X