Re: Windows и upstream php-cgi.exe
Maxim Dounin
mdounin на mdounin.ru
Пн Апр 6 12:43:07 UTC 2020
Hello!
On Mon, Apr 06, 2020 at 07:17:30AM -0400, gewisser wrote:
> Всем привет. Имеем под windows:
>
> run php-cgi.exe -b 127.0.0.1:9001-c php.ini
> run php-cgi.exe -b 127.0.0.1:9002-c php.ini
> run php-cgi.exe -b 127.0.0.1:9003-c php.ini
> run php-cgi.exe -b 127.0.0.1:9004-c php.ini
> run php-cgi.exe -b 127.0.0.1:9005-c php.ini
>
> upstream backend {
> server 127.0.0.1:9001;
> server 127.0.0.1:9002;
> server 127.0.0.1:9003;
> server 127.0.0.1:9004;
> server 127.0.0.1:9005;
> }
>
> Есть проект в котором скрипт php выполнив определённую работу, должен
> продолжить работу в фоне.
> Раньше с Apache у меня работал вот этот кусок кода:
> https://gist.github.com/bubba-h57/32593b2b970366d24be7
>
> Решил хоть как то заставить это работать с nginx. Для начала попытался
> отключить буферизацию ответов FastCGI:
> header("X-Accel-Buffering: no");
>
> И ответ стал возвращаться браузеру.
> По логике работы web приложения, в браузере к nginx идут последующие
> запросы... и вот тут то возникают для меня непонятки. Следующий запрос как
> правило (но не всегда) остаётся в режиме "pending" и остаётся до того как не
> истечёт "fastcgi_read_timeout" по умолчанию 60 сек.
>
> По какой то причине nginx зная, что один из апстримов находится в режиме
> ожидания ответа, последующий запрос ставит в очередь на этот апстрим и не
> выбирает другой... Чтобы nginx выбрал другой upstream, нужно соединение с
> браузером которое может быть keep-alive разорвать. Чтобы разорвать
> соединение нужно отправить заголовок header("Connection: close"); который
> при любых обстоятельствах не пропускается nginx брвузеру.
То, что "один из апстримов находится в режиме ожидания ответа" - в
общем случае никак не влияет на то, может ли бекенд отвечать на
другие запросы по другим соединениям. В норме - может. Если у
вас не может - нужно принимать специальные меры, чтобы
ограничивать соединения к одному бекенду. В простейшем случае -
можно использовать least_conn балансировку
(http://nginx.org/r/least_conn/ru). В вашем случае, впрочем, это
не поможет, потому что проблема в другом.
Проблема не в том, что nginx "последующий запрос ставит в очередь
на этот апстрим и не выбирает другой", проблема в том, что запрос
к бекенду - не завершён, и соответственно обработка запроса от
клиента - не завершена. Очередной запрос от клиента в том же
соединении по HTTP/1.1 - не будет обрабатываться, пока предыдущий
не завершён.
> Мы можем выставить fastcgi_read_timeout, например в 2 секунды и тогда nginx
> закроет по таймауту соединение с апстримом, скрипт успешно выполнит свою
> долгую работу в фоне, апстрим будет свободен для приёма сообщений и т.д.
> но! Что если нам реально нужно ждать ответ от апстрима более 2,3,4...100
> сек?
>
> Как можно сказать nginx закрыть соединение с fastcgi? А лучше аналогично как
> мы через хедеры говорим nginx не буферизовать вывод (X-Accel-Buffering: no),
> так же сказать закрыть соединение с апстримом.
>
> PS.
> Про FPM и fastcgi_finish_request() знаю.
Чтобы завершить обработку предыдущего запроса - нужно завершить
запрос в рамках протокола FastCGI, именно это делает
fastcgi_finish_request(). Все остальные "решения" - это костыли
той или иной степени опасности и некорректности.
Если fastcgi_finish_request() по какой-то причине не подходит -
то, вероятно, проще всего будет сделать отдельный location для
запросов, в которых предполагается долгая обработка "в фоне", и
выключить там keepalive с помощью директивы keepalive_timeout
(http://nginx.org/r/keepalive_timeout/ru).
--
Maxim Dounin
http://mdounin.ru/
Подробная информация о списке рассылки nginx-ru