Настроив балансировку HTTP-запросов nginx'ом многие упускают одну очень немаловажную деталь, которую по началу и я упускал, что приводило к очень серьёзным проблемам. И в очередной раз когда товарищ настроив балансировку на **nginx** и запоров клиента я решил написать эту заметку, про дублирование (задвоение) запросов при балансировке nginx'ом.

Вообще про балансировку запросов nginx'ом написано статей чуть больше, чем очень много, поэтому я не буду в миллионный раз описывать директивы **proxy_pass** и **upstream**, но расскажу о маленьком нюансе, про который обычно никто не пишет, по крайней мере я ни разу не встречал в распространённых статьях, на которые все ориентируются, речь идёт про встроенную в nginx систему дублирования запросов.

Мало кто задумывается о том, что произойдёт с HTTP-запросом, если он вдруг не обработается бэкэндом, или будет очень долго обрабатываться. Должен ли этот запрос отправляться на другой сервер и списка балансера, который указан в **upstream**, или же возвращать ошибку (в данном случае, речь идёт о 5xx ошибке).

При стандартных настройках **nginx** пытается переслать HTTP-запрос на следующий бэкэнд, и если на следующем бэкэнде всё прошло успешно, то пользователь даже не узнает, что были какие-либо проблемы при обработке его запроса. Может показаться, что всё супер и мы получаем отказоустойчивость, сбылась мечта программиста и сисадмина. Но если бы всё было так просто, то эта заметка не появилась на свет, потому что это поведение правильно работает только для отдачи контента, когда нам важно вернуть клиенту какую-либо запрошенную страницу. Но что будет, если это не **GET** запрос, а **POST**, который запускает какое-либо критичное с точки зрения нашего ПО действие? Например клиент оформляет заказ в нашем интернет магазине, отправляется **POST** запрос на завершение оформления заказа, а бэкэнд под нагрузкой и отвечает не так быстро как бы нам хотелось, в результате бэкэнд не отвечает нашему балансеру вовремя, и наш балансер отправляет следующему серверу этот же запрос, в результате у нас на двух серверах будет выполняться один и тот же запрос, и тут уже зависит от того как программисты реализовывали конкретный участок приложения, может быть задвоение заказа, может первый запрос обработаться, а второй отбиться с ошибкой что такой id, например, уже есть, в итоге клиент получит ошибку вместо успешного заказа и это будет выглядеть крайне странно для него. То же самое может быть и с регистрацией, и комментариями. Всё это вызывает головную боль, и самое главное, что не все понимают почему так происходит, задвоение HTTP-запросов на nginx, а искать причину этого достаточно тяжело для многих. Разработчики в таких ситуациях как правило винят IE, мол кнопка не всегда блокируется и пользователи умудряются отправить заказ два раза. QA разводят руками — воспроизвести это на dev зоне, или на preproduction, да и даже на production не возможно (в этот момент сервера могут хорошо работать, низкая нагрузка и тд).

А вся проблема сводится к настройке всего одной директивы — **proxy_next_upstream**. Она позволяет отключить передачу **POST** запросов следующему backend серверу, но это не всегда помогает, нужно очень тщательно продумывать как правильно настроить этот момент, так как, например, при разработке API не всегда очевидно как правильно настроить, но тут советов давать нет смысла, всё зависит от большого количества факторов, и от бизнес заказчиков, с которыми нужно согласовывать поведение системы.

Вывод из всего этого — RTFM!

Комментарии

comments powered by Disqus