+7 (987) 026-06-36
Написать в Telegram

Оставить заявку
stanislav_web

Ошибка 504 Gateway Timeout при запуске большого PHP скрипта из под cron

Ошибка 504 Gateway Timeout при запуске большого PHP скрипта из под cron Объясняю и показываю примеры решений разных задач с которыми сталкивался и сталкиваюсь в процессе работы

Ошибка 504 Gateway Timeout при запуске большого PHP скрипта из под cron

Объясняю и показываю примеры решений разных задач с которыми сталкивался и сталкиваюсь в процессе работы

📅 08 апреля 2024

👁‍🗨 48

🔥 0

Ошибка 504 Gateway Timeout при запуске большого PHP скрипта из под cron

Хотя запуск долгих скриптов на php (от 5 мин и более) по разным причинам обычно считается нежелательным, в некоторых ситуациях это может выручить. Простой пример: при xml-импорте инфоблока в битриксе с 500 свойствами и 10000 элементами, стандартного максимального времени выполнения может быть недостаточно - хотя скрипт и выполняется по шагам, но может, например, зависнуть, на создании вспомогательных индексов.

В данной статье рассмотрим запуск долгих php скриптов на apache+nginx и рассмотрим также ограничения браузеров.

PHP

Самое первое, что необходимо проверить - это настройки 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>