使用完整url时,PHP file_get_contents非常慢
我正在使用一个脚本(我没有创build),从HTML页面生成一个PDF文件。 问题是现在需要花费很长时间,比如1-2分钟来处理。 据说这本来工作得很好,但是在过去几周内已经放缓了。
该脚本调用php脚本中的file_get_contents
,然后将结果输出到服务器上的HTML文件中,并在该文件上运行pdf生成器应用程序。
我似乎已经将问题缩小到完整url上的file_get_contents
调用,而不是本地path。
当我使用
$content = file_get_contents('test.txt');
它几乎是即时处理的。 但是,如果我使用完整的url
$content = file_get_contents('http://example.com/test.txt');
处理需要30-90秒的时间。
它不仅限于我们的服务器,访问任何外部url(如http://www.google.com)时速度很慢。 我相信脚本调用完整的url,因为有查询stringvariables是必要的,如果您在本地调用文件不起作用。
我也尝试了fopen
, readfile
和curl
,而且都很慢。 任何想法在哪里可以解决这个问题?
注意:这已经在PHP 5.6.14中修复。
Connection: close
即使HTTP / 1.0请求也会自动发送Connection: close
标头。 见提交4b1dff6
。
我很难找出file_get_contents脚本缓慢的原因。
通过使用Wireshark进行分析,问题(在我的情况中,也可能是你的问题)是远程Web服务器在15秒之前没有closuresTCP连接(即“保持活动”)。
事实上,file_get_contents不会发送“连接”HTTP头,所以远程Web服务器默认情况下认为它是保持活动的连接,并且不会closuresTCPstream,直到15秒(可能不是标准值 – 依赖在服务器conf)。
如果HTTP有效负载长度达到了响应Content-Length HTTP标头中指定的长度,普通浏览器将认为该页面已完全加载。 File_get_contents不这样做,这是一个耻辱。
解
所以,如果你想知道解决scheme,这里是:
$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n'))); file_get_contents("http://www.something.com/somepage.html",false,$context);
只要告诉远程Web服务器在下载完成时closures连接 ,因为file_get_contents不够智能,无法自己使用响应Content-Length HTTP标头来完成。
我将使用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);
有时候,这是因为你的服务器上的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);
我遇到过同样的问题,
我唯一能做的就是在$options
数组中设置timeout。
$options = array( 'http' => array( 'header' => implode($headers, "\r\n"), 'method' => 'POST', 'content' => '', 'timeout' => .5 ), );
你能尝试在服务器上从命令行获取这个URL吗? curl或wget想到。 如果这些以正常的速度检索URL,那么这不是一个networking问题,最有可能的是在Apache / PHP的设置。
$context = stream_context_create(array('http' => array('header'=>'Connection: close\r\n'))); $string = file_get_contents("http://localhost/testcall/request.php",false,$context);
时间: 50976毫秒 (累计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数据库中获取一些数据。
我有一个庞大的数据通过API传递,我使用file_get_contents
读取数据,但它花了大约60秒 。 但是,使用KrisWebDev的解决scheme花了大约25秒 。
$context = stream_context_create(array('https' => array('header'=>'Connection: close\r\n'))); file_get_contents($url,false,$context);
我还想和Curl一起考虑的是你可以“线索”请求。 这对我非常有帮助,因为我现在还没有访问允许线程的PHP版本。
例如,我使用file_get_contents从远程服务器获取了7个图像,每个请求需要2-5秒。 单独的这个过程就是在这个过程中增加了30秒,而用户等待生成PDF。
这从字面上减less了大约1个图像的时间。 另外一个例子,我在之前的时间里做了36个url的validation。 我想你明白了。 🙂
$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]);
然后closures文件:
curl_multi_close($master);
我知道这是一个古老的问题,但我今天发现它,答案不适合我。 我没有看到有人说每个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);