Ограничение процессорной нагрузки (реализация)

Материал из 1GbWiki.

(Различия между версиями)
Перейти к: навигация, поиск
Версия 20:24, 3 апреля 2008 (править)
NovaCxarmulo (Обсуждение | вклад)
(Структура таблиц)
← К предыдущему изменению
Версия 20:26, 3 апреля 2008 (править) (отменить)
NovaCxarmulo (Обсуждение | вклад)
(Реализация)
К следующему изменению →
Строка 8: Строка 8:
Статистика нагрузки рассчитывается сервером и записывается в базу, к которой можно подключиться из своих скриптов и понять помент, в который нужно заблокировать работу сайта для предотвращения выхода им за допустимую процессорную нагрузку. Статистика нагрузки рассчитывается сервером и записывается в базу, к которой можно подключиться из своих скриптов и понять помент, в который нужно заблокировать работу сайта для предотвращения выхода им за допустимую процессорную нагрузку.
-Собственный алгоритм блокировки можно реализовать на любом из доступных языков программирования, ниже приведено подробное описание формата данных и +Собственный алгоритм блокировки можно реализовать на любом из доступных языков программирования (ASP, PERL и т.д.), ниже приведено подробное описание формата данных и наша реализация блокировки на php.
=== Формат конфигурационного файла === === Формат конфигурационного файла ===

Версия 20:26, 3 апреля 2008

Содержание

Принцип работы

Каждую минуту запускается парсер запросов к серверу, который записывет время обработки запросов в базу данных MySQL, а в начало каждого php-скрипта добавляется префикс, получающий статистику нагрузки для своего сайта и принимающий решение о блокировании дальнейшей обработки скрипта.

Сама база данных работает в отдельном экземпляре MySQL с максимально упрощенной авторизацией, все таблицы держатся в памяти, поэтому дополнительные запросы статистики не создают заметной нагрузки на сервер.

Реализация

Статистика нагрузки рассчитывается сервером и записывается в базу, к которой можно подключиться из своих скриптов и понять помент, в который нужно заблокировать работу сайта для предотвращения выхода им за допустимую процессорную нагрузку.

Собственный алгоритм блокировки можно реализовать на любом из доступных языков программирования (ASP, PERL и т.д.), ниже приведено подробное описание формата данных и наша реализация блокировки на php.

Формат конфигурационного файла

В конфигурационном для нашей реализаци используются пары ключ-значение по одному на строку в формате:

key=value

Сейчас подерживаются два параметра: FULL_BLOCK, CLIENT_BLOCK в обоих указывается процент нагрузки от одного ядра процессора.

Пример содержания файла:

FULL_BLOCK=3
CLIENT_BLOCK=0.05

Параметры для доступа к базе

Сервер: 127.0.0.1
Порт: 3399
База: ProcLimit;
Пользователь: user
Без пароля.

Структура таблиц

Список сайтов (таблица недоступна для свободного чтения)

Sites
ПолеТипПримерОписание
IDCHAR(32)6652D7688AF645EFF4FBD40B05A62C28MD5 имени сайта, буквы заглавные
NameVARCHAR(255)MYSITE.RUДоменное имя сайта заглавными буквами, без WWW.


Лог запросов (таблица не доступна для свободного чтения)

Logs
ПолеТипПримерОписание
HASHINT-1839737443Хеш строки из лог-файла, нужен для внутренних целей.
Site_IDCHAR(32)6652D7688AF645EFF4FBD40B05A62C28MD5 имени сайта, буквы заглавные
TimeDATETIME2008-04-03 21:10:15Время вызова файла скрипта
ProcessorTimeINT76Число миллисекунд процессорного времени, потраченых сервером на обработку запроса.
IPINT(4)-712745798IP-адрес, IP-адрес с которого был сделан запрос (алгоритм перевода IP-адреса в число можно посмотреть в примере реализации)


Статистика по сайту

Summary
ПолеТипПримерОписание
Site_IDCHAR(32)6652D7688AF645EFF4FBD40B05A62C28MD5 имени сайта, буквы заглавные
ProcessorTimeINT2037Число миллисекунд процессорного времени, потраченное сервером на обработку запросов к сайту за последний час.

Статистика по сайту и IP-адресу

IPSummary
ПолеТипПримерОписание
Site_IDCHAR(32)94MD5 имени сайта, буквы заглавные
IPINT367IP-адрес, с которого поступали запросы (алгоритм перевода IP-адреса в число можно посмотреть в примере реализации)
ProcessorTimeINT2037Число миллисекунд процессорного времени, потраченное сервером на обработку запросов к сайту, поступивших с этого IP-адреса.

Пример реализации на PHP

<?php

	$full_time_1gb = 60 * 60 * 1000;
	
	$path_1gb = $_SERVER["SCRIPT_FILENAME"];
	if ($path_1gb == )
		$path_1gb = $_SERVER["PATH_TRANSLATED"];
	$path_1gb = substr( $path_1gb, 0, - strlen($_SERVER['SCRIPT_NAME'])) . '/';
	
	$config_1gb = "$path_1gb/.cpu_limit.conf";
 	$logfile_1gb = "$path_1gb/.cpu_limit_".date('Y-m-d').".log";
	$logfile_1gb_debug = "$path_1gb/.cpu_limit_".date('Y-m-d')."_ok.log";

	
	$ip_1gb = $_SERVER["REMOTE_ADDR"];
	$ip_parts_1gb = @split( '\.', $ip_1gb );
	if (count ($ip_parts_1gb) != 4)
 		return;
	$ip_1gb = @intval( $ip_parts_1gb[0] ) << 24;
	$ip_1gb |= @intval( $ip_parts_1gb[1] ) << 16;
	$ip_1gb |= @intval( $ip_parts_1gb[2] ) << 8;
	$ip_1gb |= @intval( $ip_parts_1gb[3] );

	$host_1gb = @addslashes(@strtoupper($_SERVER["HTTP_HOST"]));
	if( @substr($host_1gb, 0, 4) == "WWW." )
		$host_1gb = substr( $host_1gb, 4 );
	$site_id_1gb = @strtoupper( @md5( $host_1gb ) );
	
	if( !($cfg_1gb = @file($config_1gb ) ) )
		return;
	
	$full_block_1gb = 0.50 * $full_time_1gb;
	$client_block_1gb = 0.50 * $full_time_1gb;
	foreach( $cfg_1gb as $lin1_1gb )
	{
		$lparts_1gb = @split("=", $lin1_1gb);
		if( count( $lparts_1gb) != 2 )
			continue;
		$name_1gb = strtoupper( trim( $lparts_1gb[0] ) );
		$val_1gb = trim( $lparts_1gb[1] );
		
		if( $name_1gb == "FULL_BLOCK" )
			$full_block_1gb = @floatval( $val_1gb ) / 100 * $full_time_1gb;
		if( $name_1gb == "IP_BLOCK" )
			$client_block_1gb = @floatval( $val_1gb ) / 100 * $full_time_1gb;
	}
	
	$con_1gb = @mysql_connect( "127.0.0.1:3399", "user" );
 	if( !$con_1gb )
	{
		if( $logfile = @fopen( $logfile_1gb_debug, "at+" ) )
		{
			@fwrite( $logfile, date( "Y-m-d H:i:s" ) . ", accounting database is offline\n" );
			@fclose( $logfile );
		}
		return;	
	}
	@mysql_query( "use ProcLimit;", $con_1gb);

	
 	$q_1gb = "SELECT ProcessorTime FROM Summary WHERE Summary.Site_ID = '$site_id_1gb'";
	$res_1gb = @mysql_query( $q_1gb, $con_1gb );
	if( $res_1gb )
		$res_1gb = @mysql_fetch_row( $res_1gb );

	if( $res_1gb )
 	{
		$load_from_ip_1gb = @round ($res_1gb[0] * 100 / $full_time_1gb, 2);
		if( $res_1gb[0] >= $full_block_1gb )
		{
			if( $logfile = @fopen( $logfile_1gb, "at+" ) )
			{
				@fwrite( $logfile, date( "Y-m-d H:i:s" ) . ", blocked: $_SERVER[REMOTE_ADDR] ($load_from_ip_1gb %)\n" );
				@fclose( $logfile );
			}
			die( "Сервер перегружен, попробуйте зайти позже" );
 		}
	}

				
	$q_1gb = "SELECT ProcessorTime FROM IPSummary WHERE IPSummary.Site_ID = '$site_id_1gb' AND IPSummary.IP = '$ip_1gb'";
	$res_1gb = @mysql_query( $q_1gb, $con_1gb );
	if( $res_1gb )
		$res_1gb = @mysql_fetch_row( $res_1gb );
	
	if( $res_1gb )
	{
		$load_total_1gb = @round ($res_1gb[0] * 100 / $full_time_1gb, 2);
		if( $res_1gb[0] > $client_block_1gb )
		{
			if( $logfile = @fopen( $logfile_1gb, "at+" ) )
			{
 				@fwrite( $logfile, date( "Y-m-d H:i:s" ) . ", blocked: $_SERVER[REMOTE_ADDR] (IP load = $load_total_1gb %)\n" );
				@fclose( $logfile );
			}
			die( "Сервер перегружен, попробуйте зайти позже" );
		}
	}

	
 	if( $logfile = @fopen( $logfile_1gb_debug, "at+" ) )
	{
		@fwrite( $logfile, date( "Y-m-d H:i:s" ) . ", ok $_SERVER[REMOTE_ADDR] (IP load = $load_total_1gb %, total = $load_from_ip_1gb %)\n" );
		@fclose( $logfile );
	}
	
	
	unset( 
		$path_1gb, $config_gile, $ip_1gb, $host_1gb, $logfile_1gb, $logfile_1gb_debug, $logfile, $cfg_1gb, $full_1gb, $full_block_1gb, $client_block_1gb, 
		$lin1_1gb, $lparts_1gb, $name_1gb, $val_1gb, $q_1gb, $res_1gb, $ip_parts_1gb, $full_time_1gb, $load_from_ip_1gb, $load_total_1gb, $site_id_1gb
		);
	
?>
Личные инструменты