У меня есть Nginx 1.4.4 и PHP 5.5.6. Я делаю запросы на длительный опрос. Проблема в том, что если я отменяю HTTP-запрос, отправленный через Ajax, запросы все еще обрабатываются (они не останавливаются). Я тестировал его с помощью функции PHP mail () в конце файла, а почта все еще идет, и файл не останавливался).
Я беспокоюсь, потому что я думаю, что это может привести к сбою сервера из-за большой нагрузки незакрытых запросов. Да, я попробовал ignore_user_abort(false);
но без изменений. Возможно, что я должен что-то изменить в Nginx?
location ~ \.php$ { try_files $uri =404; include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; }
Плохая новость заключается в том, что вы почти наверняка не сможете решить свою проблему, как вы хотите ее решить. Сигнал FastCGI, отправленный, когда клиент закрывает соединение до получения запроса, является FCGI_ABORT_REQUEST
Веб-сервер прерывает запрос FastCGI, когда клиент HTTP закрывает свое транспортное соединение, а запрос FastCGI работает от имени этого клиента. Ситуация может показаться маловероятной; большинство запросов FastCGI будут иметь короткое время отклика, причем веб-сервер обеспечивает буферизацию вывода, если клиент работает медленно. Но приложение FastCGI может задерживаться в общении с другой системой или при нажатии на сервер.
К сожалению, похоже, что ни оригинальная реализация fast-cgi, ни PHP-FPM не поддерживают сигнал FCGI_ABORT_REQUEST и поэтому не могут быть прерваны.
Хорошая новость заключается в том, что есть лучшие способы решения этой проблемы. В принципе, у вас никогда не должно быть запросов, требующих много времени для обработки. Вместо этого, если запрос требует много времени для обработки, вы должны:
В дополнение к этим 3 основным вещам – если вы обеспокоены потерей системных ресурсов, когда клиент больше не интересуется результатами запроса, вы должны добавить:
Вы не говорите, какова ваша давно работающая задача – давайте притворимся, что это загрузка большого файла изображения с другого сервера, управление этим изображением, а затем его сохранение в S3. Таким образом, состояния для этой задачи будут выглядеть примерно так:
TASK_STATE_QUEUED TASK_STATE_DOWNLOADING //Moves to next state when finished download TASK_STATE_DOWNLOADED TASK_STATE_PROCESSING //Moves to next state when processing finished TASK_STATE_PROCESSED TASK_STATE_UPLOADING_TO_S3 //Moves to next state when uploaded TASK_STATE_FINISHED
Поэтому, когда клиент отправляет исходный запрос, он возвращает идентификатор taskID, а затем, когда он запрашивает состояние этой задачи, либо:
или
т.е.
TASK_STATE_QUEUED => TASK_STATE_DOWNLOADING TASK_STATE_DOWNLOADED => TASK_STATE_PROCESSING TASK_STATE_PROCESSED => TASK_STATE_UPLOADING_TO_S3
Таким образом, только запросы, которые интересует клиент, продолжают обрабатываться.
btw Я настоятельно рекомендую использовать что-то, предназначенное для работы в качестве очереди для хранения очереди задач (например, Rabbitmq , Redis или Gearman ), а не просто с использованием MySQL или любой базы данных. В принципе, SQL просто не так хорош в том, чтобы действовать как очередь, и вам было бы лучше использовать соответствующую технологию с самого начала, вместо того, чтобы использовать неправильную технологию для запуска, а затем придется ее заменять в чрезвычайной ситуации, когда ваша база данных становится перегружен, когда он пытается сделать сотни вложений, обновлений в секунду для управления задачами.
В качестве побочного эффекта, разбирая долгий процесс обработки задач, становится очень легко:
Что именно вы делаете в этих длительных запросах? Если все, что вы делаете, заставляет процесс FastCGI ждать какого-либо системного вызова, например, ожидая возвращения базы данных в результате, прерванное соединение HTTP-клиента не приведет к прерыванию этого вызова. Если я правильно помню, эффект ignore_user_abort(false)
заключается только в том, что PHP-скрипт прерывается, как только он пытается вывести что-то в (теперь потерянное) соединение. Сценарий не будет писать какой-либо вывод, пока он ждет системного вызова.
Если возможно, вы должны разделить задачу, выполняемую длинным сценарием, на более мелкие куски и проверить статус соединения между их обработкой. Убедитесь, что сценарий завершается, если соединение было прервано:
while (!$done_yet) { if(connection_status() != CONNECTION_NORMAL) { break; } do_more_work(); }
В документации PHP вы найдете дополнительную информацию о подключении, если хотите.