【问题标题】:How to limit the amount of connections per second made to server in GET / PHP5?如何限制 GET / PHP5 中每秒与服务器建立的连接数?
【发布时间】:2011-03-27 02:26:44
【问题描述】:

鉴于下面的脚本可以获取另一种语言的单词并与服务器建立连接。

但是,有太多单独的字符串实体,其中一些返回为空值。 StackOverflow 研究员@Pekka 正确评估了 Google 的局限性:结果超时。

Q1.如何使连接更强大/更可靠,尽管以速度为代价?
Q2.如何刻意限制每秒与服务器建立的连接数?

只要返回的值正确,我愿意牺牲速度,即使它会导致 120 秒的延迟)。现在一切都在 0.5 秒左右开始和结束,翻译中有各种差距。几乎就像荷兰奶酪(有洞),我想要没有洞的奶酪,即使这意味着更长的等待时间。

如您所见,我自己的让脚本休眠 1/4 秒的解决方案不能称为优雅......如何从这里开始?

    $url='http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=' . rawurlencode($string) . '&langpair=' . rawurlencode($from.'|'.$to);
    $response   = file_get_contents($url,
            null,
            stream_context_create(
            array(
            'http'=>array(
            'method'=>"GET",
            'header'=>"Referer: http://test.com/\r\n"
            )
            )
        ));
usleep(250000); # means 1/4 of second deliberate pauze
return self::cleanText($response);
}

【问题讨论】:

    标签: php api get


    【解决方案1】:

    如何故意限制每秒与服务器建立的连接数?

    这取决于。在理想的世界中,如果您期望任何级别的流量,您可能希望您的抓取工具成为您通过message or work queue 与之通信的守护程序。在这种情况下,守护进程将能够严格控制每秒的请求数并适当地节流。

    听起来您实际上是根据用户请求进行的。老实说,你目前的睡眠策略还不错。当然,它是“粗制滥造”,但它很简单而且很有效。当您可能有多个用户同时发出请求时,麻烦就来了,在这种情况下,这两个请求将不知道另一个请求,并且您最终每秒收到的请求数将超过服务允许的数量。

    这里有一些策略。如果 URL 永远不会改变,也就是说,你只是限制一个服务,你基本上需要一个 semaphore 来协调多个脚本。

    考虑使用一个简单的锁定文件。或者,更准确地说,锁定文件上的文件锁定:

    // Open our lock file for reading and writing; 
    // create it if it doesn't exist, 
    // don't truncate, 
    // don't relocate the file pointer.
    $fh = fopen('./lock.file', 'c+');
    foreach($list_of_requests as $request_or_whatever) {
    // At the top of the loop, establish the lock.
        $ok = flock($fh, LOCK_EX):
        if(!$ok) {
            echo "Wow, the lock failed, that shouldn't ever happen.";
            break; // Exit the loop.
        }
    // Insert the actual request *and* sleep code here.
        $foo->getTranslation(...);
    // Once the request is made and we've slept, release the lock
    // to allow another process that might be waiting for the lock
    // to grab it and run.
       flock($fh, LOCK_UN);
    }
    fclose($fh);
    

    这在大多数情况下都能正常工作。如果您使用的是超低成本或低质量的共享主机,由于底层文件系统(不)的工作方式,锁可能会适得其反。 flock is also a bit finicky on Windows.

    如果您要处理多项服务,事情就会变得有些棘手。我的第一直觉是在数据库中创建一个表并开始跟踪发出的每个请求,如果在过去 Z 秒内对域 Y 发出的请求超过 X 个,则添加额外的限制。

    Q1.我怎样才能使连接更强大/更可靠,尽管以速度为代价?

    如果您坚持使用 Google 翻译,您可能需要switch to the Translate v2 RESTful API。这需要一个 API 密钥,但注册过程将迫使您通过他们的 TOS,这应该记录他们的请求/期限最大限制。由此,您可以将系统请求限制在其服务将支持并保持可靠性的任何速率。

    【讨论】:

      【解决方案2】:

      您可以从较短的等待时间开始,只有在您没有得到响应时才增加它。像这样的。

      $delay = 0;
      $i = 0;
      $nStrings = count($strings);
      
      while ($i < $nStrings) {
          $response = $this->getTranslation($strings[$i]);
          if ($response) {
              $i++;
              # save the response somewhere
          else {
              $delay += 1000;
              usleep($delay);
          }
      }
      

      【讨论】:

        【解决方案3】:

        这是我在my CURL wrapper 上使用的代码的 sn-p,延迟呈指数级增加 - 这是一件好事,否则您最终可能只会给服务器施加压力而永远得不到积极的响应:

        function CURL($url)
        {
            $result = false;
        
            if (extension_loaded('curl'))
            {
                $curl = curl_init($url);
        
                if (is_resource($curl))
                {
                    curl_setopt($curl, CURLOPT_FAILONERROR, true);
                    curl_setopt($curl, CURLOPT_AUTOREFERER, true);
                    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
                    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        
                    for ($i = 1; $i <= 8; ++$i)
                    {
                        $result = curl_exec($curl);
        
                        if (($i == 8) || ($result !== false))
                        {
                            break;
                        }
        
                        usleep(pow(2, $i - 2) * 1000000);
                    }
        
                    curl_close($curl);
                }
            }
        
            return $result;
        }
        

        这里的 $i 变量的最大值为 8,这意味着该函数将尝试获取 URL 总共 8 次,延迟时间分别为 0.5、1、2、4、8、16、32 和 64 秒(或总共 127.5 秒)。

        对于并发进程,我建议使用 APC 或类似的设置共享内存变量。

        希望对你有帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-05-20
          • 1970-01-01
          • 2021-08-24
          • 2011-01-26
          • 1970-01-01
          • 1970-01-01
          • 2012-10-17
          • 1970-01-01
          相关资源
          最近更新 更多