file_get_contents()是一个阻塞函数吗?

5

我正在通过file_get_contents连接到一个不可靠的API。 既然它是不可靠的,我决定将api调用放入一个while循环中:

$resultJSON = FALSE;

while(!$resultJSON) {
    $resultJSON = file_get_contents($apiURL);
    set_time_limit(10);
}

换句话说:如果API在第三次尝试成功之前失败了两次,我是发送了3个请求,还是在那个3秒的时间窗口内发送了数百个请求?

我认为这是set_time_limit的一个打字错误。(PHP内置函数。) - user149341
1
啊,set_time_limit(10) 的意思是,“如果你在这个 while 循环中仍然卡住了超过 10 秒,就放弃执行。” - patricksayshi
据我所知,在URL上使用file_get_contents()通常是一个非常糟糕的想法。在您的情况下,您应该使用cURL和一个或多个超时选项 - Sammitch
@Sammitch 谢谢。不过我很好奇file_get_contents()在处理URL时有什么问题。 - patricksayshi
1
如果您想要内容(响应),那么它是阻塞的。在这里设置时间限制是毫无意义的。 - Dejan Marjanović
4个回答

9

file_get_contents(),就像 PHP 中的基本所有函数一样,是一个阻塞调用。


1
如果你想等待响应,就像在这种情况下他想要的那样。 - Dejan Marjanović
该调用没有非阻塞版本。(即使有也没有意义。) - user149341
3
file_get_contentsfopen/fread/fclose 的缩写,同时支持 URL。但这不是 PHP 读取 URL 的唯一方式(不使用 cURL 等)。可以通过 fsockopenSTREAM_CLIENT_ASYNC_CONNECT 和/或 stream_set_blocking 实现非阻塞模式。你的回答没有问题,我只是说一下。干杯! - Dejan Marjanović

2

是的,它是一个阻塞函数。您还应该检查值是否特别为“false”。(请注意使用===而不是==。)最后,您需要休眠10秒钟。set_time_limit()用于设置在自动终止之前的最大执行时间。

set_time_limit(300); //Run for up to 5 minutes.

$resultJSON = false;
while($resultJSON === false)
{
    $resultJSON = file_get_contents($apiURL);
    sleep(10);
}

它确实返回false。从手册中可以看到:“该函数在失败时返回读取的数据或FALSE。”http://php.net/manual/en/function.file-get-contents.php。编辑:哦,抱歉,没事了,我现在明白你的意思了。 - patricksayshi
2
是的,但你的检查没有排除假值。在这种情况下使用 "!" 太宽泛了,因为可能会返回内容。 - Jordan Mack
第二个 resultJSON 中缺少的 $ 是故意的吗? - user1111929

1

在 @Sammitch 建议使用 cURL 而不是 file_get_contents() 的基础上进行扩展:

<?php
$apiURL = 'http://stackoverflow.com/';

$curlh = curl_init($apiURL);
// Use === not ==
// if ($curlh === FALSE) handle error;
curl_setopt($curlh, CURLOPT_FOLLOWLOCATION, TRUE); // maybe, up to you
curl_setopt($curlh, CURLOPT_HEADER, FALSE); // or TRUE, according to your needs
curl_setopt($curlh, CURLOPT_RETURNTRANSFER, TRUE);
// set your timeout in seconds here
curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($curlh, CURLOPT_TIMEOUT, 30);
$resultJSON = curl_exec($curlh);
curl_close($curlh);
// if ($resultJSON === FALSE) handle error;
echo "$resultJSON\n"; // Now process $resultJSON
?>

还有很多 curl_setopt 选项。你应该去查看一下。

当然,这假设 你已经安装了 cURL


1

我不知道PHP中是否有任何不会“阻塞”的函数。作为替代方案,如果您的服务器允许这样做,您可以:

  1. 使用pcntl_fork()在等待API调用完成时在脚本中执行其他操作。
  2. 如果无法使用pcntl_fork(),则使用exec()在后台调用另一个脚本[使用&]来为您执行API调用。

但是,如果您的脚本没有成功调用该API就不能执行任何其他操作,那么该调用是否“阻塞”并不重要。您真正需要关注的是等待此API所花费的时间是否超过了配置的max_execution_time,导致您的脚本在中途被中止而未能正确完成。

$max_calls = 5;
for( $i=1; $i<=$max_calls; $i++ ) {
    $resultJSON = file_get_contents($apiURL);
    if( $resultJSON !== false ) {
        break;
    } else if( $i = $max_calls ) {
        throw new Exception("Could not reach API within $max_calls requests.");
    }
    usleep(250000); //wait 250ms between attempts
}

值得注意的是,file_get_contents()默认超时时间为60秒,因此您的脚本很可能会被终止。强烈考虑改用cURL,因为您可以设置更合理的超时值。

API保持一致性,能够及时返回响应,因此我认为不需要担心60秒超时限制。问题在于,大约三分之一的时间响应是500内部服务器错误。我正在寻找一种方法,在避免陷入循环的可能性的同时继续请求响应。你在这里提供的代码非常好地解决了那个问题,谢谢! - patricksayshi
1
啊,原来是那种不可靠的。尽管如此,我仍然建议使用cURL,因为您可以检索HTTP响应代码,并在获取除200或500之外的其他内容时采取行动,这表明出现了与通常不同的问题。 - Sammitch

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