- PVSM.RU - https://www.pvsm.ru -
Иногда возникает необходимость писать скрипты, работа которых занимает продолжительное время. Например, скрипты создания/развертывания бэкапов, установки демо-версии какого-то приложения, агрегирования больших объемов данных, импорта/экспорта данных и т.п. Для того, чтобы такие скрипты не прекращали свою работу в неожиданный момент, нужно знать и помнить о некоторых вещах.
В первую очередь нужно установить подходящее значение параметра max_execution_time [1] в конфиге PHP.
Если скрипт запускается веб-сервером (т.е. в ответ на HTTP-запрос от пользователя), то следует также правильно настроить параметры таймаута в конфиге веб-сервера. Для apache это параметры TimeOut [2] и FastCgiServer… -idle-timeout ... [3] (если PHP работает через FastCGI), для nginx send_timeout [4] и fastcgi_read_timeout [5] (если PHP работает через FastCGI).
Веб-сервер может также проксировать запросы на другой веб-сервер, который и запустит PHP скрипт (не редкий пример, nginx — фронтенд, apache — бэкэнд). В этом случае на проксирующем веб-сервере необходимо также настраивать таймаут проксирования. Для apache ProxyTimeout [6], для nginx proxy_read_timeout [7].
Если скрипт запускается в ответ на HTTP-запрос, то пользователь может остановить выполнение запроса в своем браузере, в этом случае прекратит свою работу и PHP скрипт. Если же требуется, чтобы скрипт продолжил свою работу даже после остановки запроса, установите в TRUE параметр ignore_user_abort [8] в конфиге PHP.
Если в скрипте открывается соединение с каким-либо сервисом/службой (с БД, с почтовым сервером, с FTP-сервером, ...), и во время выполнения скрипта некоторое время соединение не используется, то оно может быть закрыто этим сервисом. Например, если во время работы скрипта некоторое время не выполнять запросы к MySQL, то MySQL закроет соединение через время, заданное в параметре wait_timeout [9]. Как следствие, при попытке выполнить очередной запрос возникнет ошибка.
В таких случаях следует проверять активность соединения, в тех местах кода, где возможны простои его использования, и переподключаться при необходимости. Например в модуле MySQLi есть полезная функция mysqli::ping [10] для проверки активности соединения, а также параметр конфигурации mysqli.reconnect [11] для автоматического переподключения, при разрыве соединения. При отсутствии подобных функций для других видов соединений, можно попробовать написать ее самому. В ней нужно тривиальным образом обратиться к сервису (например, выполнить запрос SELECT 1 FROM dual), и в случае ошибки (отловить при помощи try… catch ...) переподключиться.
Нередко долгие скрипты запускаются по расписанию (по cron), и ожидается, что в один момент времени будет работать только одна копия скрипта. Но может случиться так, что очередной запуск скрипта произойдет раньше, чем закончит работу предыдущий, и как правило это нежелательно (дважды импортируются одни и те же данные, затрутся данные используемые первым скриптом, ...).
В таких случаях можно использовать блокировку используемых ресурсов, но эта задача всегда решается индивидуально. Либо можно просто проверять, не запущена ли другая копия этого скрипта, и либо подождать завершения его работы, либо завершить текущий запуск. Для этого можно просматривать список запущенных процессов, либо использовать блокировку запуска самого скрипта, что то вроде:
if (lockStart('script.php'))
{
// основной код скрипта
...
lockStop('script.php');
}
В случаях, когда долгие скрипты запускаются через веб-сервер, соединение клиента с этим самым веб-сервером остается открытым до тех пор, пока не отработает скрипт. Это не есть хорошо, т.к. задача веб-сервера как можно быстрее обработать запрос и отдать результат. Если же соединение остается висеть, то один из воркеров (процессов) веб-сервера на долгое время будет занят. А если одновременно будет запущено достаточно много таких скриптов, то они могут занять все (ну или почти все) свободные воркеры (для apache см. MaxClients [12]), и веб-сервер просто не сможет обрабатывать другие запросы.
Поэтому следует при обработке запроса пользователя, запускать скрипт в фоновом режиме через php-cli, чтобы не нагружать веб-сервер, а пользователю отвечать что его запрос обрабатывается. При необходимости можно периодически проверять состояние обработки при помощи AJAX запросов.
Вот, пожалуй, и все что я могу рассказать по этой теме. Надеюсь, для кого-то будет полезным.
Автор: gegokk
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/php-2/31325
Ссылки в тексте:
[1] max_execution_time: http://www.php.net/manual/ru/info.configuration.php#ini.max-execution-time
[2] TimeOut: http://httpd.apache.org/docs/2.2/mod/core.html#timeout
[3] FastCgiServer… -idle-timeout ...: http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html#FastCgiServer
[4] send_timeout: http://nginx.org/ru/docs/http/ngx_http_core_module.html#send_timeout
[5] fastcgi_read_timeout: http://nginx.org/ru/docs/http/ngx_http_fastcgi_module.html#fastcgi_read_timeout
[6] ProxyTimeout: http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#proxytimeout
[7] proxy_read_timeout: http://nginx.org/ru/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
[8] ignore_user_abort: http://www.php.net/manual/ru/misc.configuration.php#ini.ignore-user-abort
[9] wait_timeout: http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout
[10] mysqli::ping: http://www.php.net/manual/ru/mysqli.ping.php
[11] mysqli.reconnect: http://www.php.net/manual/en/mysqli.configuration.php#ini.mysqli.reconnect
[12] MaxClients: http://httpd.apache.org/docs/2.2/mod/mpm_common.html#maxclients
[13] Источник: http://habrahabr.ru/post/175651/
Нажмите здесь для печати.