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;