使用完整网址时,PHP file_get_contents非常慢

时间:2010-09-02 17:16:51

标签: php

我正在处理一个从HTML页面生成pdf文件的脚本(我之前没有创建)。问题是现在需要花费很长时间,比如1-2分钟来处理。据说这最初工作正常,但在过去几周内已经放缓。

脚本在php脚本上调用file_get_contents,然后将结果输出到服务器上的HTML文件中,并在该文件上运行pdf生成器应用程序。

我似乎已将问题缩小到file_get_contents调用完整网址,而不是本地路径。

当我使用

$content = file_get_contents('test.txt');
它几乎是即时处理的。但是,如果我使用完整的URL

$content = file_get_contents('http://example.com/test.txt');

需要30-90秒才能处理。

它不仅限于我们的服务器,访问任何外部网址时速度很慢,例如http://www.google.com。我相信脚本会调用完整的url,因为如果在本地调用文件,则需要查询字符串变量。

我还尝试了fopenreadfilecurl,他们都同样很慢。关于在哪里解决此问题的任何想法?

9 个答案:

答案 0 :(得分:172)

  

注意:这已在PHP 5.6.14中修复。现在,即使对于HTTP / 1.0请求,也会自动发送Connection: close标头。 See commit 4b1dff6

我很难弄清楚file_get_contents脚本缓慢的原因。

通过Wireshark分析,问题(在我的情况下,也可能是你的)是远程网络服务器在15秒内没有关闭TCP连接(即“保持活着”)。

实际上,file_get_contents不会发送“连接”HTTP标头,因此远程Web服务器默认情况下认为它是保持活动连接,并且在15秒之前不会关闭TCP流(可能不是标准值 - 取决于服务器配置。)

如果HTTP有效负载长度达到响应Content-Length HTTP标头中指定的长度,普通浏览器会认为页面已完全加载。 File_get_contents没有这样做,这是一种耻辱。

<强>解

所以,如果你想知道解决方案,那就是:

$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n')));
file_get_contents("http://www.something.com/somepage.html",false,$context);

事情只是告诉远程Web服务器在下载完成时关闭连接,因为file_get_contents不够智能,无法使用响应Content-Length HTTP头自行完成

答案 1 :(得分:35)

我会使用curl()来获取外部内容,因为这比file_get_contents方法快得多。不确定这是否能解决问题,但值得一试。

另请注意,您的服务器速度将影响检索文件所需的时间。

以下是一个使用示例:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://example.com/test.txt');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = curl_exec($ch);
curl_close($ch);

答案 2 :(得分:5)

有时,这是因为服务器上的DNS速度太慢,请尝试以下方法:

替换

echo file_get_contents('http://www.google.com');

作为

$context=stream_context_create(array('http' => array('header'=>"Host: www.google.com\r\n")));
echo file_get_contents('http://74.125.71.103', false, $context);

答案 3 :(得分:3)

我有同样的问题,

唯一对我有用的是在$options数组中设置超时。

$options = array(
    'http' => array(
        'header'  => implode($headers, "\r\n"),
        'method'  => 'POST',
        'content' => '',
        'timeout' => .5
    ),
);

答案 4 :(得分:1)

您可以尝试从命令行在服务器上获取该URL吗?卷曲或wget浮现在脑海中。如果那些以正常速度检索URL,那么它不是网络问题,而且很可能是apache / php设置中的内容。

答案 5 :(得分:0)

$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n')));
$string = file_get_contents("http://localhost/testcall/request.php",false,$context);

时间: 50976 ms (平均5次尝试的空间时间)

$ch = curl_init();
$timeout = 5;
curl_setopt($ch, CURLOPT_URL, "http://localhost/testcall/request.php");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
echo $data = curl_exec($ch);
curl_close($ch);

时间: 46679毫秒(平均5次尝试次数)

注意:request.php用于从mysql数据库中获取一些数据。

答案 6 :(得分:0)

我有一个由API传递的大量数据,我使用file_get_contents来读取数据,但它花了 60秒。但是,使用KrisWebDev的解决方案需要 25秒

$context = stream_context_create(array('https' => array('header'=>'Connection: close\r\n')));
file_get_contents($url,false,$context);

答案 7 :(得分:0)

我还会考虑使用Curl,您可以“线程化”请求。这对我帮助很大,因为我无法访问允许线程化的PHP版本。

例如,我使用file_get_contents从远程服务器获取了7个图像,每个请求需要2-5秒。仅此过程就会向进程添加30秒或其他内容,同时用户等待生成PDF。

这实际上减少了大约1张图像的时间。另一个例子,我在做一个之前的时间里验证了36个网址。我认为你说对了。 : - )

    $timeout = 30;
    $retTxfr = 1;
    $user = '';
    $pass = '';

    $master = curl_multi_init();
    $node_count = count($curlList);
    $keys = array("url");

    for ($i = 0; $i < $node_count; $i++) {
        foreach ($keys as $key) {
            if (empty($curlList[$i][$key])) continue;
            $ch[$i][$key] = curl_init($curlList[$i][$key]);
            curl_setopt($ch[$i][$key], CURLOPT_TIMEOUT, $timeout); // -- timeout after X seconds
            curl_setopt($ch[$i][$key], CURLOPT_RETURNTRANSFER, $retTxfr);
            curl_setopt($ch[$i][$key], CURLOPT_HTTPAUTH, CURLAUTH_ANY);
            curl_setopt($ch[$i][$key], CURLOPT_USERPWD, "{$user}:{$pass}");
            curl_setopt($ch[$i][$key], CURLOPT_RETURNTRANSFER, true);
            curl_multi_add_handle($master, $ch[$i][$key]);
        }
    }

    // -- get all requests at once, finish when done or timeout met --
    do {  curl_multi_exec($master, $running);  }
    while ($running > 0);

然后检查结果:

            if ((int)curl_getinfo($ch[$i][$key], CURLINFO_HTTP_CODE) > 399 || empty($results[$i][$key])) {
                unset($results[$i][$key]);
            } else {
                $results[$i]["options"] = $curlList[$i]["options"];
            }
            curl_multi_remove_handle($master, $ch[$i][$key]);
            curl_close($ch[$i][$key]);

然后关闭文件:

    curl_multi_close($master);

答案 8 :(得分:0)

我知道这是一个古老的问题,但我今天发现它并且答案对我不起作用。我没有看到有人说每个IP的最大连接数可能设置为1.这样你就是在做API请求而API正在做另一个请求因为你使用了完整的URL。这就是直接从光盘加载的原因。对我来说,解决了一个问题:

if (strpos($file->url, env('APP_URL')) === 0) {
    $url = substr($file->url, strlen(env('APP_URL')));
} else {
    $url = $file->url;
}
return file_get_contents($url);