我正在处理一个从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,因为如果在本地调用文件,则需要查询字符串变量。
我还尝试了fopen
,readfile
和curl
,他们都同样很慢。关于在哪里解决此问题的任何想法?
答案 0 :(得分:172)
注意:这已在PHP 5.6.14中修复。现在,即使对于HTTP / 1.0请求,也会自动发送
Connection: close
标头。 See commit4b1dff6
我很难弄清楚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);