在PHP中设置Curl的超时时间

278

我正在通过PHP对eXist数据库运行curl请求。数据集非常大,因此数据库始终需要很长时间才能返回XML响应。为了解决这个问题,我们设置了一个curl请求,并设置了一个应该很长的超时时间。

$ch = curl_init();
$headers["Content-Length"] = strlen($postString);
$headers["User-Agent"] = "Curl/1.0";

curl_setopt($ch, CURLOPT_URL, $requestUrl);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERPWD, 'admin:');
curl_setopt($ch,CURLOPT_TIMEOUT,1000);
$response = curl_exec($ch);
curl_close($ch);

然而,通过curl请求时请求在完成之前就持续结束(<1000当通过浏览器请求时)。请问是否知道这是设置curl超时的正确方式?

7个回答

426

请参阅文档:http://www.php.net/manual/zh/function.curl-setopt.php

CURLOPT_CONNECTTIMEOUT - 尝试连接时等待的秒数。使用0可无限等待。
CURLOPT_TIMEOUT - 允许cURL函数执行的最大秒数。

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 0); 
curl_setopt($ch, CURLOPT_TIMEOUT, 400); //timeout in seconds

还要不要忘记将PHP脚本的执行时间延长:

set_time_limit(0);// to infinity for example

19
如果脚本在控制台运行,就不需要使用set_time_limit(0); - Pedro Lobito
7
@PedroLobito,您提到的是php在cli上的默认配置,但是有可能已经被修改过了。 - cherouvim
4
@cherouvim 在这里显然是正确的(只需运行 php -d max_execution_time=1 -r 'while(true){$r=1*1;}' 或类似命令来观察,就会发现 CLI 并没有一个神奇的“永远无限制”的标志)。 - Wrikken
@Pedro Lobito,如果你不在循环内使用它,就不需要set_time_limit(0) - Viktor Joras
5
CURLOPT_CONNECTTIMEOUT = 0并不意味着“无限期”,而是将连接超时值设置为默认值-300秒。 - C.L.

61

嗯,我觉得 CURLOPT_TIMEOUT 定义了任何 cURL 函数允许执行的时间。我认为你实际上应该查看 CURLOPT_CONNECTTIMEOUT,因为它告诉 cURL 等待连接完成的最长时间。


虽然 PHP 文档 中提到 CURLOPT_TIMEOUT 是指函数运行的时间,但是底层 curl 库文档似乎是指请求的时间,这是一个有趣的区别 - 不确定该如何理解! - fideloper
我认为这里是最好的解释:https://dev59.com/3V4c5IYBdhLWcg3wj7B6 - fideloper

50

这里有一个小问题,可能对某些人很重要…… 摘自PHP文档评论。

如果您想让cURL在一秒钟内超时,可以使用CURLOPT_TIMEOUT_MS,然而在“类Unix系统”上,存在一个错误/“特性”,如果值小于1000毫秒,则会导致libcurl立即超时并显示错误“cURL Error(28):超时已达到”。这种行为的解释是:

“如果libcurl构建为使用标准系统名称解析器,则该传输的那部分仍将使用全秒分辨率进行超时,最小允许超时时间为1秒。”

对PHP开发人员来说,这意味着“您不能在未经测试之前使用该函数,因为您无法确定libcurl是否使用标准系统名称解析器(但您可以非常确定它正在使用)”

问题在于,在(Li | U)nix上,当libcurl使用标准名称解析器时,名称解析期间会引发SIGALRM,而libcurl认为这是超时警报。

解决方案是使用CURLOPT_NOSIGNAL禁用信号。以下是一个示例脚本,请求自身并导致10秒延迟,以便您可以测试超时:

if (!isset($_GET['foo'])) {
    // Client
    $ch = curl_init('http://localhost/test/test_timeout.php?foo=bar');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200);
    $data = curl_exec($ch);
    $curl_errno = curl_errno($ch);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($curl_errno > 0) {
        echo "cURL Error ($curl_errno): $curl_error\n";
    } else {
        echo "Data received: $data\n";
    }
} else {
    // Server
    sleep(10);
    echo "Done.";
}

来自http://www.php.net/manual/en/function.curl-setopt.php#104597


你好,这段代码可以运行,但源文件大小为7MB,而下载的文件只有52KB,可能出了什么问题?URL类似于http://webserver.tld/folder/download/file.do?name=file.zip&_lg=en_US。 - Muflix
@Simon East,你能帮我吗?http://stackoverflow.com/questions/30861112/scrap-amazon-all-deals-php-curl - Padmanathan J
请注意,使用此脚本时您应该预计会发生超时错误。 - kmoney12

34
你的代码将超时时间设置为1000。如果要使用毫秒,请使用CURLOPT_TIMEOUT_MS

16

在你和文件之间,比如PHP和Curl,需要确保超时时间。

如果要告诉Curl当传输仍在进行时不要超时,需要将CURLOPT_TIMEOUT设置为0而非1000

curl_setopt($ch, CURLOPT_TIMEOUT, 0);
在PHP中,你必须移除时间限制,否则PHP本身(默认情况下30秒后)会杀死Curl请求的脚本。 仅此就可以解决你的问题。此外,如果您需要数据完整性,可以使用ignore_user_abort添加一层安全保障。
# The maximum execution time, in seconds. If set to zero, no time limit is imposed.
set_time_limit(0);

# Make sure to keep alive the script when a client disconnect.
ignore_user_abort(true);

客户端断开连接将中断脚本的执行并可能损坏数据,例如:非过渡性数据库查询、构建配置文件等。在您的情况下,它会下载部分文件......你可能关心也可能不关心。

回答这个旧问题是因为这个线程在搜索 CURL_TIMEOUT 时排名靠前。


8

您无法在浏览器中运行请求,因为它会超时等待运行CURL请求的服务器响应。浏览器可能会在1-2分钟内超时,这是默认的网络超时时间。

您需要在命令行/终端中运行它。


2
+1 -- 超时可能是curl外部的问题。您可以通过确保定期输出某些内容来解决浏览器超时的问题;浏览器通常在接收到更多数据时重置其超时时间。但这是一种hack方法;通过CLI运行(几乎?)总是更可取的。 - Frank Farmer

3
如果您正在使用PHP作为fastCGI应用程序,请确保检查fastCGI超时设置。 参考链接:PHP curl put 500 error

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接