2013-02-27 2 views
1

У меня есть функция, которые посылают запрос HTTP на www.server.comPHP Предотвращение выполнения функции синхронной (дросселировании пределов через PHP)

Моей задачу с помощью CURL, чтобы убедиться, что www.server.com получает не более один запрос каждые 2 секунды.

Возможное решение:

Создайте функцию checktime(), которая будет хранить текущее время вызова в базе данных и проверить с базой данных на каждом следующем вызове и сделать паузу системы в течение 2-х секунд:

$oldTime = $this->getTimeFromDatabase(); 
if ($oldTime < (time() - 2)) { // if its been 2 seconds 
    $this->setNewTimeInDatabase(); 
    return true; 
} else { 
    sleep(2); 
    return false; 
} 

проблемы/вопрос:

Скажем, последний запрос на www.server.com был на 1361951000. Затем 10 других пользователей попытаются выполнить запрос по телефону 1361951001 (1 секунда спустя). checktime() Функция будет называться.

Поскольку, поскольку прошло только 1 секунда, функция вернет значение false. Все 10 пользователей будут ждать 2 секунды. Означает ли это, что на 1361951003 будет отправлено 10 запросов одновременно? И возможно ли, что время последнего запроса не будет изменено в базе данных из-за пропущенного вызова $ this-> setNewTimeInDatabase() в checktime()?

Спасибо!

UPDATE:

Я только что сказали, что с помощью цикла может решить эту проблему:

for($i=0;$i<300;$i++) 
{ 
    $oldTime = $this->getTimeFromDatabase(); 
    if ($oldTime < (time() - 2)) { // if its been 2 seconds 
    $this->setNewTimeInDatabase(); 
    return true; 
    } else { 
    sleep(2); 
    return false; 
    } 
} 

Но я не вижу логики в этом.

+0

Не было бы лучше сделать проверку на 'www.server.com'? – Zaffy

+0

@ Zaffy У меня нет доступа к 'www.server.com' – rinchik

+0

Другой вариант - создать статическую переменную, которая работает как очередь, а затем через 2 секунды следующий элемент из очереди отправляется на сервер. ком. Это должно быть статической переменной, поэтому она будет использоваться для всех экземпляров вашего класса. – Mike

ответ

1

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

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

$oldTime = $this->getTimeFromDatabase(); 
if ($oldTime < (time() - 2) && $this->getLock()) { // if its been 2 seconds 
    $this->setNewTimeInDatabase(); 
    $this->releaseLock(); 
    return true; 
} else { 
    sleep(2); 
    return false; 
} 

function getLock() 
{ 
    return $mysqli->query('UPDATE locktable set locked = 1 WHERE locked = 0'); 
} 

function releaseLock() 
{ 
    $mysqli->query('UPDATE locktable set locked = 0'); 
} 

Я не уверен, о функциях MySQL, но я считаю, что это нормально, чтобы получить общее представление.

+0

Хммм .. что, если я просто проверю, произошло ли обновление БД? 'if ($ oldTime <(time() - 2) && $ this-> setNewTimeInDatabase())' – rinchik

+0

Ну, на самом деле мне придется изменить свой первоначальный ответ, чтобы сделать его более явным, но принцип стоит. Идея использования базы данных как механизма блокировки заключается в обеспечении атомарности (я имею в виду, чтобы гарантировать, что только один поток получает возможность выполнять операцию в любой момент времени). Если вы не реализуете такой механизм, вы не можете быть уверены, что только один из них должен установитьNewTime. – Muc

+0

спасибо! Я реализовал столовые замки :) – rinchik

1

Остерегайтесь использования базы данных. Например, MySQL не всегда на 100% синхронизируется со своими сеансами, и по этой причине небезопасно полагаться на это для целей блокировки.

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

Было бы, вероятно, пойти что-то вроде этого:

$filepath = "lockfile_for_source"; 
touch($filepath); 
$fp = fopen("lockfile_for_resource", "r") or die("Could not open file."); 

while(true){ 
    while(!flock($fp, LOCK_EX)){ 
    sleep(0.25); //wait to get file-lock. 
    } 

    $time = file_get_contents($filepath); 
    $diff = time() - $time; 
    if ($diff >= 2){ 
    break; 
    }else{ 
    flock($fp, LOCK_UN); 
    } 
} 

//Following code would never be executed simultaneously by two scripts. 
//You should access and use your resource here. 

fwrite($fp, time()); 
fflush($fp); 
flock($fp, LOCK_UN); //remove lock on file. 
fclose($fp); 

Пожалуйста, обратите внимание, что я не проверял код.

+0

Это также может работать, но если вам нужно сделать запрос с нескольких разных серверов, локальный файл не будет работать. – Muc

+1

В этом случае распределенная блокировка решит проблему. Для этого потребуется создать такую ​​систему, как [flaco-lock-service] (https://code.google.com/p/flaco-lock-service/) Или можно установить общий ресурс, который будет разделяемый между серверами, а затем перейти с файловым замком. – kaspernj

+0

Я не могу настроить какие-либо системы или смонтировать все. Мне нужно решить это в двух строках кода :) – rinchik