Ошибка 504 Gateway Timeout при запуске большого PHP скрипта из под cron
Объясняю и показываю примеры решений разных задач с которыми сталкивался и сталкиваюсь в процессе работыОшибка 504 Gateway Timeout при запуске большого PHP скрипта из под cron
Объясняю и показываю примеры решений разных задач с которыми сталкивался и сталкиваюсь в процессе работы
📅 08 апреля 2024
👁🗨 293
Хотя запуск долгих скриптов на php (от 5 мин и более) по разным причинам обычно считается нежелательным, в некоторых ситуациях это может выручить. Простой пример: при xml-импорте инфоблока в битриксе с 500 свойствами и 10000 элементами, стандартного максимального времени выполнения может быть недостаточно - хотя скрипт и выполняется по шагам, но может, например, зависнуть, на создании вспомогательных индексов.
В данной статье рассмотрим запуск долгих php скриптов на apache+nginx и рассмотрим также ограничения браузеров.
Самое первое, что необходимо проверить - это настройки php "max_execution_time" и "session.gc_maxlifetime" (максимальное время выполнения скрипта и время сессии соответственно). Ее можно установить из htaccess (параметры устанавливаются в секундах):
php_value max_execution_time 2400
php_value session.gc_maxlifetime 2400
а также непосредственно из php скрипта:
set_time_limit(2400);
ini_set('session.gc_maxlifetime', 2400);
или, конечно же, из php.ini
NGINXОднако, даже если установить большое значение max_execution_time, при установленном nginx, если время еще не истекло, вы можете получить сообщение об ошибке подобное этому:
504 Gateway Timeout error using NginxДанную ситуацию можно исправить, отредактировав файл конфигурации nginx (на системах Debian находится здесь - /etc/nginx/nginx.conf):
proxy_connect_timeout 2400;
proxy_send_timeout 2400;
proxy_read_timeout 2400;
Но и это еще не все. У браузеров также есть свои ограничения по времени на получение страниц и у разных браузеров они отличаются. Но это также поправимо, и проще всего эта настройка меняется в браузере Mozilla Firefox: нужно зайти в расширенные настройки введя в адресной строке "about:config", затем найти по поиску значение "network.http.connection-timeout", в котором, как и везде, можно установить значение максимального времени получения страницы в секундах.
Скрипты тестирования максимального времени выполнения скрипта
Для тестирования описанных мной настроек, я приведу здесь два скрипта - простой запуск страницы и получение с использованием ajax (это может быть важно, т.к. настройки браузера могут отличаться для получения простых страниц и средствами XHR).
Простой скрипт:
<?
$ts_start = time();
$cur_sec = 0;
while(true) {
$seconds = time() - $ts_start;
if($seconds%20==0 && $cur_sec!=$seconds) {
echo $seconds."<br />";
$cur_sec = $seconds;
}
if($seconds>=2350)
break;
}
?>
Скрипт с применением ajax:
<?
if(array_key_exists("ajax_test",$_REQUEST) && $_REQUEST["ajax_test"]=="Y") {
$ts_start = time();
$cur_sec = 0;
while(true) {
$seconds = time() - $ts_start;
if($seconds%20==0 && $cur_sec!=$seconds) {
echo $seconds."<br />";
$cur_sec = $seconds;
}
if($seconds>=30)
break;
}
die();
}
?><html>
<head><title>time limit test</title></head>
<body>
<a href="javascript:window.start_ajax_test();">Start ajax test</a> <span id="counter"></span>
<div id="container" style="border: 1px solid green"></div>
<script>
window.is_send = false;
window.start_ajax_test = function() {
if(window.is_send)
return false;
document.getElementById('container').innerHTML = 'Loading...';
window.is_send=true;
window.counter_start = new Date();
window.counter = setInterval(function(){
var end = new Date();
secondsOpen = Math.floor((end - window.counter_start) / 1000);
document.getElementById('counter').innerHTML = secondsOpen + ' seconds';
}, 250);
window.ajax_request(
'<?=$_SERVER['REQUEST_URI']?>',
'POST',
function(response_text) {
window.is_send=false;
document.getElementById('container').innerHTML = response_text;
clearInterval(window.counter);
document.getElementById('counter').innerHTML += " total";
},
{ 'ajax_test' : 'Y' }
);
};
window.ajax_request = function(url, requestType, callback, arParams) {
var xhr;
var ajax_params = function(arParams, prefix) {
var str = [];
for(var p in arParams) {
if (arParams.hasOwnProperty(p)) {
var k = prefix ? prefix + "[" + p + "]" : p, v = arParams[p];
str.push(typeof v == "object" ?
ajax_params(v, k) :
encodeURIComponent(k) + "=" + encodeURIComponent(v));
}
}
return str.join("&");
}
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
var protocols = ['MSXML2.XMLHTTP.3.0','MSXML2.XMLHTTP','Microsoft.XMLHTTP'];
for (var pc = 0; pc < protocols.length; pc++) {
try {
xhr = new ActiveXObject(protocols[pc]);
break;
} catch(e) {}
}
}
xhr.onreadystatechange = function() {
if(xhr.readyState==4)
{
callback(xhr.responseText);
xhr = null;
}
};
var data = ajax_params(arParams);
if (requestType.toUpperCase() === 'GET') {
if (data.length > 0)
url += (url.indexOf('?') == -1?'?':'&') + data;
xhr.open('GET', url, true);
xhr.send("");
} else if (requestType.toUpperCase() === 'POST') {
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);
}
};
</script>
</body>
</html>