Пропустить навигацию.
Home

Умный запуск из крона, смотрим нагрузку сервера

Когда на сервере, или его кусочке - VDS, живут кроновые процессы (для СЕО продвижения, генерации контента и пр.) есть шансы не расчитать их интенсивность и положить сервер. Вообще-то, такие шансы есть и при хорошем просчете нагрузки, когда возникают временные затыки в сети/мускуле/прочем, и даже время, установленной посредством set_time_limit() оказывается превышенным.

 

 

Один из вариантов контроля за процессами состоит в независимой проверке времени выполнения, о чем я расскажу позже. Другой вариант - недопущение запуска скриптов из крона при критических условиях.

Сначала расскажу о том, как у меня организованы кроновые скрипты. Основная (программерская) проблема при их запускепроистекает из того, что скрипт должен вызываться как из коммандной строки (то есть вне апача), так и из браузера (для отладки). Эти два запуска происходят от имени разных юзеров, соответственно должен быть организован доступ к файлам - записав что-либо в файл (например, логи) не забудьте сделать ему chmod 0755, иначе запуски скрипта, пишушего в этот же файл под другим юзером слеят с Access Denied

Далее, у каждого уважающего себя софт-девелопера есть наборы тулзов, которые хранятся в отдельной папке и подключаются к скриптам без указания пути к этой папке, благодаря директиве include_path, установленной в файле .htaccess или php.ini. НО! При запуске из крона не задействуется апач и не обрабатывается .htaccess. Мне не нравится использование php.ini для этой цели, поскольку оно требует лишнего телодвижения при переезде на другой хостинг, а локальные php.ini могут и не обрабатываться при особо "удачной" настройке php (что опять-таки, потребует телодвижений).

А устанавливаю include_path я в специальном файле с названием cron.inc.php такого вида:

 

	
	if (!isset($_SERVER['REMOTE_ADDR'])) 
	{
		ini_set('include_path',"./:../inc");
		define('from_cron',1);
	}
	else
		define('from_cron',0);

 

Этот файл находится в каждой папке, содержащей кроновые скрипты. Как видим, тут заодно устанавливается константа from_cron, которая может пригодиться.

Таким образом, структура кронового скрипта такова:

include "cron.inc.php"; 
include "моитулзы.inc.php";
// processing...

 ... и файл моитулзы.inc.php будет успешно подключаться как из браузера, так и из крона.

Вот в этом файле cron.inc.php мы и будем проверять текущую нагрузку сервера:

 

<?


class Cron_init
{
	var $max_scripts_count = 1;  	// max number of scipts with the same names
	var $max_loading = 0.2; 		// max CPU loading (from uptime command)
	var $min_iddle = 70; 			// min CPU iddle
	var $file2log = false;			// file to log 
	
	function init()
	{
		$vmstat = `vmstat`;
		if ($vmstat)
		{
			$vmstat = explode("\n",$vmstat);
			$vmstat_keys = preg_split('/\s+/s',$vmstat[1]);
			$vmstat_values = preg_split('/\s+/s',$vmstat[2]);
			$vmstat = array();
			for($i=0; $i<count($vmstat_keys); $i++)
			{
				$vmstat[$vmstat_keys[$i]] = $vmstat_values[$i];
			}
			if ($vmstat['id']<$this->min_iddle) $this->cancel('insufficient CPU iddle level');
		}
		$uptime = `uptime`; // format:  04:58:26 up 8 days, 18:19,  1 user,  load average: 0.00, 0.00, 0.00
		$m = preg_match_all('/\d{1,2}\.\d\d/i',$uptime,$machos);
		if ($m)
		{
			list($one,$five,$fifteen) = $machos[0];
			if ($one>$this->max_loading) $this->cancel('CPU loading');
		}
		$ps = `ps axu`;
		$m = preg_match_all("|\d{1,2}:\d{2}[^\n;]+".$_SERVER['PHP_SELF']."[^\n;]*|",$ps,$machos);
		if (!$m)
			$clones = 0;
		else
			$clones = count($machos[0])-1;
		if ($clones>=$this->max_scripts_count) $this->cancel("limit of copies of the script: $clones copy(s) found");
	}
	
	function cancel($reason)
	{
		if ($this->file2log) 
		{
			error_log("\n".date("r")." Cancelling $_SERVER[PHP_SELF] due to $reason\n",3,$this->file2log);
			@chmod($this->file2log,0755);
		}
		die();
	}
}


	if (!isset($_SERVER['REMOTE_ADDR'])) 
	{
		ini_set('include_path',"./:../inc");
		define('from_cron',1);
		$_ci = new Cron_init();
		$_ci->file2log = "../logs/croninit.log";
		$_ci->init();
	}
	else
		define('from_cron',0);
?>

 

Как видим, здесь добавился класс Cron_init. Его задача - снять текущий скрипт если параметры нагрузки сервера превышают заданные. Параметры такие:

max_scripts_count - максимально допустимое количество копий текущего скрипта.

max_loading - максимально допустимая загрузка сервера (в терминах  комманды uptime - средняя длина очереди процессов)

min_iddle - минимальный уровень тунеядства CPU :)

file2log - файл, в который будут писаться сообщения о снятии скриптов с дистанции.

Разумеется, для функционирования скрипта необходим доступ php к коммандной строке.