Удивительно, что человек ленится потратить несколько лет на собственное дело, но не ленится десятилетиями ходить по утрам на работу

NGINX: Ошибка 504 Gateway Timeout и тяжелые запросы в PHP

Думаю большинство из нас осознаёт, для чего существуют механизмы таймаутов в различных компонентах единой рабочей системы. К примеру, в PHP есть такой параметр, как max_execution_time, а в веб сервере Nginx есть fastcgi_read_timeout, определяющие границы для данных механизмов. Система должна быть отзывчивой и должна уметь определять рамки своих временных квот выделяемых на обработку каких-либо действий.

На примере вышеназванных max_execution_time и fastcgi_read_timeout, по умолчанию, мы имеем не более 30 секунд между ответами PHP скрипта (запущенного через CGI, не из командной строки), такое правило диктует max_execution_time, и не более 60 секунд между ответами FastCGI сервера (к примеру php-fpm), это определяет fastcgi_read_timeout. Таким образом, если у нас что-то "задумется" и не пришлёт каких-либо данных в течение более 30 секунд, то система потеряет всякий интерес к этому что-то и прекратит его обслуживание. Однако, если это что-то будет напоминать о себе хотя-бы каждые 28 секунд, то оно может жить в системе очень долго.

Как правило, такие ограничения по таймаутам вполне себе комфортны для штатной работы, однако, как нам поступать в нештатных случаях, когда необходимо выполнить тяжелый запрос/работу расширив рамки ограничений?

Первое, и неверное решение, что приходит в голову, воспользоваться в тяжелом PHP скрипте функцией set_time_limit, чтобы изменить max_execution_time прямо во время выполнения. Однако, это необходимо делать для случая, когда мы получаем ошибку вида:


Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/html/index.php on line N

А нам необходимо победить ошибку 504 Gateway Timeout, которая возникает на уровне веб сервера, и в нашем случае это уровень Nginx. Для этого, в конфигурации Nginx, заведём отдельную область через location, для которой установим желательные значения fastcgi_read_timeout и, если необходимо, max_execution_time и все тяжелые запросы перенаправим на данную конфигурацию. К примеру, для всех запросов к /long_running_script_uri/ мы можем установить таймаут до 3-х минут, вот таким образом:


location ~ ^/long_running_script_uri/(.+)$ {
    alias /var/www/html/$1;

    ...
    fastcgi_read_timeout 180s;
    fastcgi_param PHP_VALUE "max_execution_time=0";
    ...
    fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
    ...
}

Таким образом, обратившись к скрипту, (к примеру к script.php) по обычному URI: /script.php мы получим настройки таймаута по умолчанию, а обратившись к нему же через URI: /long_running_script_uri/script.php наши рамки ограничений на выполнение будут расширены, и мы сможем выполнять наш скрипт не менее 3-х минут.

Ответить
Обязательные поля помечены *