【问题标题】:PHP Prevent simultaneous function execution (throttling limits via PHP)PHP 防止同时执行函数(通过 PHP 限制)
【发布时间】:2013-02-13 19:24:52
【问题描述】:

我有一个通过 CURL 向 www.server.com 发送 HTTP 请求的函数

我的任务是确保 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()的未接调用,数据库中最后一次请求的时间不会改变?

谢谢!

更新:

我刚刚被告知使用循环可能会解决问题:

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;
   }
}

但我并没有真正看到其中的逻辑。

【问题讨论】:

  • 检查www.server.com会不会更好?
  • @Zaffy 我无权访问www.server.com
  • 另一种选择是创建一个像队列一样工作的静态变量,然后在 2 秒后,队列中的下一个项目被发送到 server.com。这必须是一个静态变量,以便在您的类的所有实例之间共享
  • 为什么不只缓存 2 秒 ttl 的响应?
  • @Crisp 我对每个请求都会得到不同的响应

标签: php multithreading concurrency call throttling


【解决方案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 的功能我不确定,但我相信大致了解一下就可以了。

【讨论】:

  • 嗯.. 如果我只检查数据库更新是否发生怎么办? if ($oldTime &lt; (time() - 2) &amp;&amp; $this-&gt;setNewTimeInDatabase())
  • 好吧,实际上我必须更改我原来的答案以使其更明确,但原则是成立的。使用数据库作为锁定机制的想法是确保原子性(我的意思是,确保在任何时候只有一个线程可以执行操作)。如果你不实施这样的机制,你就不能确定只有一个人去 setNewTime。
【解决方案2】:

注意使用数据库。例如,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);

请注意我没有测试过代码。

【讨论】:

  • 这也可以,但是如果您需要从多个不同的服务器进行查询,则本地文件将无法执行。
  • 在这种情况下,分布式锁可以解决问题。这将需要为类似flaco-lock-service 的系统设置一个系统,或者可以安装一个将在服务器之间共享的共享,然后使用文件锁。
  • 我无法设置任何系统或安装任何东西。我需要在几行代码内解决它:)
  • 如果你有多个服务器,那么我想不出除了分布式锁或共享文件系统锁之外的任何其他解决方案。如果您在单个服务器上,那么我认为文件锁是最好的。
  • 你好。应该是两个不同的文件名:lockfile_for_source 和 lockfile_for_resource 吗?
猜你喜欢
  • 2010-12-28
  • 1970-01-01
  • 2010-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-15
  • 1970-01-01
  • 2015-12-25
相关资源
最近更新 更多