С тех пор, как мы перешли от простого экземпляра Apache к среде с балансировкой нагрузки, иногда возникают проблемы с повторением запросов POST. Мы запускаем nginx как обратный прокси. Статический контент обслуживается самим nginx, а динамический контент обслуживается двумя бэкэндами Apache.
Я проверил, что это не ошибка интерфейса / пользователя. Небольшой пример: простая загрузка изображения приведет к тому, что изображение будет загружено дважды. Запрос / POST не отправляется дважды при двойном щелчке или ошибке пользователя. Я не нашел никаких доказательств того, что браузер отправляет запрос дважды, поэтому я подозреваю, что это на стороне сервера. (Обратите внимание, что это только подозрение.) Большинство этих запросов являются внутренними, то есть они исходят от сотрудников, поэтому я могу проверить, как они приходят.
Единственная ошибка, которую я могу найти, это то, что nginx регистрирует 499
ошибка в этих случаях. Однако я не уверен, является ли это причиной или (побочным) эффектом проблемы. (Я знаю, что 499 - это не статус http по умолчанию, это статус nginx, означающий «клиент закрыл соединение»)
Повторные POST-запросы - это почти все запросы, которые могут занять некоторое время. То, что я показываю здесь в качестве примера, - это простая загрузка изображения, но сценарий выполняет некоторые действия в фоновом режиме (изображение должно быть преобразовано в разные форматы / размеры и должно быть распределено на оба сервера и т. Д.).
Примером может служить загрузка изображения. nginx зарегистрирует один запрос «499» и один запрос 200, но Apache получает (и обрабатывает!) два запроса.
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
Мне кажется, что большие / медленные загрузки страдают от этого больше, поэтому я подозреваю тайм-аут. Я попытался прочитать ошибку 499: похоже, что это «клиентское закрытое соединение». Это могло произойти в фоновом режиме, но я не уверен, как это может означать, что должен быть выдан второй запрос, и определенно не происходит чего-то вроде «закрытого пользователем браузера».
В настоящее время кажется, что это помогает разбить более медленные запросы POST (если нужно сделать несколько вещей, просто заставьте пользователя выбрать 1 и POST второй раз для другого), но это может просто снизить вероятность того, что это произойдет. Точно сказать не могу.
Очевидно, это временное решение. Если это является тайм-аут, мне нужно выяснить, где и увеличить соответствующие числа, но я не уверен, почему тайм-аут может вызвать такое поведение: я подозреваю, что сообщение «ну что ж, пошло не так», а не повторение.
Я хочу выяснить, какой процесс / ситуация может вызвать повторение POST. Конечно, любое «не знаю почему, но это будет исправлено увеличением этого тайм-аута» тоже замечательно.
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;
}
Я удалил несколько строк, относящихся к IP, в geo
части, а также SSL
вариации, чтобы не усложнять. При необходимости я могу заменить их, но все сводится к дополнительному geo
часть для бэкэндов ssl и соответствующие файлы upstream и 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;
}
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;
Краткий ответ: попробуйте это для своего блока местоположения:
location / {
proxy_read_timeout 120;
proxy_next_upstream error;
proxy_pass http://$backend;
}
Более длинное объяснение:
Я думаю, что столкнулся именно с той проблемой, которую вы описали:
499
статус для этих запросов, и один и тот же запрос появляется в разных вышестоящих узлахОказывается, на самом деле это поведение по умолчанию для nginx в качестве обратного прокси, поэтому обновление его до более высоких версий не решит эту проблему, хотя это было указано как возможное решение. Вот, но это решает другую проблему.
Это происходит потому, что nginx в качестве балансировщика нагрузки выбирает вышестоящий узел в круговая мода. Когда выбранный узел выходит из строя, запрос отправляется следующему узлу. Здесь важно отметить, что отказ узла по умолчанию классифицируется как error or timeout
. Поскольку вы не установили proxy_read_timeout
, значение по умолчанию - 60 секунд. Таким образом, после 60 секунд ожидания nginx выбирает следующий узел и отправляет тот же запрос.
Таким образом, одним из решений является увеличение этого тайм-аута, чтобы ваша длительная операция могла завершиться, например установив proxy_read_timeout 120;
(или любой другой предел, соответствующий вашим потребностям).
Другой вариант - запретить обратному прокси-серверу пытаться использовать следующий узел в случае тайм-аута, установив proxy_next_upstream error;
. Или вы можете установить оба этих параметра, как предложено выше.
Из эта тема форума мы узнали, что виновником может быть SPDY. Для этого пользователя кажется решением отключить его, и у нас не было двойных сообщений с момента его отключения.
Точная проблема, кроме «SPDY сделал это», на данный момент неизвестна, побочные эффекты предложенного решения (отключить SPDY), очевидно, «больше не SPDY», но мы можем с этим жить.
Пока ошибка не всплывет снова, я называю это «исправлением» (или, по крайней мере, решением проблемы).
edit: Мы (25-02-2014) больше не видели, чтобы эта проблема возникала, так что это действительно похоже на долгосрочное решение.