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