Ограничение на выполнение одного экземпляра php-файла с одного IP

IDr

Новичок
Ограничение на выполнение одного экземпляра php-файла с одного IP

Подобные темы неоднократно поднимались но не в одной я не нашел решения своей задачи.

Есть у меня страница с несколькими рекламными блоками (от 2 до 8), рекламные блоки сделаны в <iframe> ведущими на рекламный сервер. Соответственно когда открывается страница с рекламой, начинают подгружаться <iframe>. Причем иногда (около 5% случаев) подгруздка идет одновременно (миллисекунда в миллисекунду). Получается что один скрипт не знает о выполнении второго и они могут вывести один и тот же баннер одновременно, т.к. проверка на то показан уже баннер на этой странице или нет, не работает, ведь проверка на показанность баннера в двух скриптах запускается параллельно.
Мне кажется, что наиболее рациональный вариант это создать ограничение на выполнение только одного экземпляра этого скрипта с одного IP.
Пробовал вариант с mod_limitipconn на Apache, но этот вариант не подходит, в случае исчерпания лимита он выдает «503 Service Temporarily Unavailable», а мне нужно, что бы скрипты просто становились в очередь.

-~{}~ 30.11.06 17:02:

Решил проблему следующим образом (используя MySQL):

Создал таблицу:
Код:
CREATE TABLE `activeip_tmp` (
  `active_ip` int(10) unsigned NOT NULL default '0',
  `lock_time` int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`active_ip`)
) ENGINE=MEMORY DEFAULT CHARSET=cp1250;
т.к она «ENGINE=MEMORY» работает очень быстро...

В начале скрипта написал следующий код:
PHP:
$wait_time=24; //sec.
$client_ip = ( !empty($HTTP_SERVER_VARS['REMOTE_ADDR']) ) ? $HTTP_SERVER_VARS['REMOTE_ADDR'] : ( ( !empty($HTTP_ENV_VARS['REMOTE_ADDR']) ) ? $HTTP_ENV_VARS['REMOTE_ADDR'] : getenv('REMOTE_ADDR') );
$ipnum = sprintf("%u", ip2long($client_ip));
	
$locked=false;
for($for_start=microtime(true);(microtime(true)-$for_start)<($wait_time+5); )
{	
	$query_ip_test="SELECT `active_ip`, (UNIX_TIMESTAMP()-`lock_time`) as locked_sec FROM `activeip_tmp` WHERE `active_ip`=$ipnum";
	$ip_ar=$GLOBALS["db"]->myquery($query_ip_test);
	if (!is_array($ip_ar)) 
	{	my_error_log(0,"Не могу выполнить запрос:\n".$query_ip_test,__FILE__,__LINE__);
		die();	
	}

	if (count($ip_ar)>0)
	{
		// На случай если скрипт был прерван и сам не удалил запись о блокировке
		if ($ip_ar[0]["locked_sec"]>$wait_time) 
		{	
			my_error_log(0,"Принудительно снимаем блокировку $wait_time сек.:",__FILE__,__LINE__);
			$del_query="DELETE FROM activeip_tmp WHERE (UNIX_TIMESTAMP()-`lock_time`) > $wait_time ";
			$GLOBALS["db"]->myquery($del_query);	
		}
	}else 
	{
		$insert_query="INSERT
                    	INTO activeip_tmp(`active_ip`, `lock_time`)
                    	VALUES ('$ipnum', UNIX_TIMESTAMP())";
		
		$result=$GLOBALS["db"]->myquery($insert_query);
		if ($result>0) 
		{
			$locked=true;
			break;	
		}			
	}
	
	usleep(50000); //0.05 cек
}


// На случай если цикл закончится по таймеру ($wait_time+5), 
// может быть вызвано только ошибкой в коде или проблемой с базой.
if (!$locked) 
{	
	my_error_log(0,"Заблокировать не удалось за ".($wait_time+5)."сек. (ошибка работы кода):",__FILE__,__LINE__);
}

В конце скрипта:
PHP:
if ($locked) 
{
	$del_query="DELETE FROM activeip_tmp WHERE `active_ip` = $ipnume ";
	$GLOBALS["db"]->myquery($del_query);	
}
Все работает и довольно быстро.

Если есть другие варианты решения данной проблемы или вы считаете это решение плохим - прошу высказываться.

PS Заметил интересную штуку, бываю люди у которых грузятся фреймы через разные IP. То есть первый фрейм через один IP второй фрейм через другой. На 4000 показов 2 таких человека прошло.
 

С.

Продвинутый новичок
Вообще-то разработчики серверов кровь из носа стараются распараллелить запросы, а тебе вот в очередь их понадобилось. Что-то наверное не так...

Предлагаю формировать список запрашиваемых баннеров в родительском скрипте, а в <iframe> уже подставлять их конкретные ID.
 

IDr

Новичок
Автор оригинала: С.
Предлагаю формировать список запрашиваемых баннеров в родительском скрипте, а в <iframe> уже подставлять их конкретные ID.
Такой вариант не подходит, рекламный сервер отдельный и обслуживает не один сайт...
 

С.

Продвинутый новичок
Автор оригинала: IDr
рекламный сервер отдельный и обслуживает не один сайт...
Технически, это совершенно никак не мешает реализации подобного варианта. Понятно, что надо дорабатывать протокол обмена с сервером. Большинство рекламных серверов отдают по одному банеру, поэтому и пользуются упрощенным протоколом. Те, кто берет по два и более могут получить одинаковые (посмотри, сплошь и рядом). Тогда либо они не заморачиваются по этому поводу, либо должны реализовать другой протокол.

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

IDr

Новичок
Автор оригинала: С.
Технически, это совершенно никак не мешает реализации подобного варианта. Понятно, что надо дорабатывать протокол обмена с сервером. Большинство рекламных серверов отдают по одному банеру, поэтому и пользуются упрощенным протоколом. Те, кто берет по два и более могут получить одинаковые (посмотри, сплошь и рядом). Тогда либо они не заморачиваются по этому поводу, либо должны реализовать другой протокол.
Нет ну конечно можно написать некий протокол обмена с рекламным сервером, и при генерировании скриптом странице запросить с рекламного сервера список баннеров. Но это плохой вариант т.к. скорость работы основного сайта будет зависеть от скорости работы рекламного сервера, что не есть правильно.

Есть другой вариант: делать скрытый фрейм, в котором подгружать информацию с рекламного сервера и разбрасывать ее по рекламным местам JavaScript-ом. Но этот вариант мне тоже не нравится т.к. он все-таки браузерозваисимый. А тестировать JavaScript это слишком дорогое удовольствие, я ж не гугл с тысячами тестеров...

Так что я лучшего варианта не нашел.

Кстати по результатам тестов скорость обработки в среднем так и осталась 15-25 миллисекунд, а в 5% случаев оно подскакивает 300-400 миллисекунд, что в принципе в пределах нормы…
 
Сверху