2013-02-27 89 views
1

我有一个功能,通过卷曲发送HTTP请求到www.server.comPHP防止同时执行功能(通过节流PHP限制)

我的任务是确保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个请求会同时发送?是否有可能在数据库中不会更改最后一次请求的时间,因为在checktime()中错过了$ this-> setNewTimeInDatabase()的调用?

谢谢!

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 2013-02-27 20:11:00

+0

@Zaffy我无法访问'www.server.com' – rinchik 2013-02-27 20:11:39

+0

另一种方法是创建一个静态变量,它像一个que一样工作,然后在2秒钟后,来自que的下一个项目被发送到服务器。 COM。这必须是一个静态变量,因此它可以在你的类的所有实例中共享。 – Mike 2013-02-27 20:12:35

回答

1

我相信你需要一些执行semaphore。数据库可以工作,只要你能保证只有一个线程可以写入数据库然后发出请求。

例如,您可能会使用对数据库的更新请求,然后检查更新的行(以检查更新是否真的发生)。如果更新成功,您可以假设您获得了互斥锁,然后发出请求(假定时间是正确的)。这样的事情:

$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 2013-02-27 20:57:27

+0

呃,实际上我必须改变我原来的答案,以便使它更加明确,但原则是站得住脚的。使用数据库作为锁定机制的想法是确保原子性(我的意思是,确保只有一个线程可以在任何时候执行操作)。如果你没有实现这样的机制,你不能确定只有一个人需要设置NewTime。 – Muc 2013-03-15 14:40:32

+0

谢谢!我实现了表锁:) – rinchik 2013-03-15 15:02:47

1

小心使用数据库。例如,MySQL并不总是与它的会话同步,因此,为了锁定目的,依赖它是不安全的。

您可以通过方法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 2013-02-27 20:36:05

+1

在这种情况下,分布式锁将解决问题。这需要建立一个像[flaco-lock-service](https://code.google.com/p/flaco-lock-service/) 这样的系统,或者可以安装一个共享在服务器之间共享,然后使用文件锁定。 – kaspernj 2013-02-27 20:54:54

+0

我不能设置任何系统或安装任何东西。我需要在几行代码中解决它:) – rinchik 2013-02-27 20:59:09