与CURL Multi PHP不一致

时间:2014-03-20 16:39:18

标签: php curl

当我对10个网址运行检查时,如果我能够与主机服务器建立连接,则句柄将返回成功消息(CURLE_OK)

如果服务器拒绝连接,处理每个句柄时,句柄将包含错误消息。

问题

我假设当我们得到一个坏句柄时,CURL会标记这个句柄,但继续处理未处理的句柄,但这似乎不会发生。 当我们遇到错误的句柄时,CURL会将此句柄标记为错误,但不会处理剩余的未处理句柄。

这很难检测,如果我确实得到了所有句柄的连接,这是大多数时间发生的事情,那么问题就不可见了。(CURL只在第一次连接错误时停止);

对于测试,我必须找到一个合适的站点,加载缓慢/拒绝x量同时连接。

set_time_limit(0);

$l = array(
    'http://smotri.com/video/list/',
    'http://smotri.com/video/list/sports/',
    'http://smotri.com/video/list/animals/',
    'http://smotri.com/video/list/travel/',
    'http://smotri.com/video/list/hobby/',
    'http://smotri.com/video/list/gaming/',
    'http://smotri.com/video/list/mult/',
    'http://smotri.com/video/list/erotic/',
    'http://smotri.com/video/list/auto/',
    'http://smotri.com/video/list/humour/',
    'http://smotri.com/video/list/film/'
);


$mh = curl_multi_init();

$s = 0;
$f = 10;

while($s <= $f)
{   

    $ch = curl_init();  

    $curlsettings = array(
        CURLOPT_URL => $l[$s],
        CURLOPT_TIMEOUT => 0,
        CURLOPT_CONNECTTIMEOUT => 0,
        CURLOPT_RETURNTRANSFER => 1
    );

    curl_setopt_array($ch, $curlsettings);
    curl_multi_add_handle($mh,$ch);

    $s++;

    }

$active = null;

do 
{
    curl_multi_exec($mh,$active);
    curl_multi_select($mh);

    $info = curl_multi_info_read($mh);

    echo '<pre>';
    var_dump($info);

    if($info['result'] === CURLE_OK)
        echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' success<br>';

    if($info['result'] != 0)
        echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' failed<br>';

} while ($active > 0);

curl_multi_close($mh);

我已经在脚本中转储了$ info,询问Multi Handle是否在运行时有任何句柄的新信息。 当脚本结束时,我们将看到一些bool(false) - 当没有新信息可用时(句柄仍处理),以及所有句柄如果一切成功或有限句柄,如果一个句柄失败。

我未能解决这个问题,这可能是我忽略的事情,而且我试图修复那些不相关的事情,我已经走得太远了。

解决这个问题的一些尝试是。

  • 将每个$ ch句柄分配给一个数组 - $ ch [1],$ ch [2]等(而不是 将当前$ ch句柄添加到multi_handle然后覆盖 - 如何 在测试中)

  • 成功/失败后删除句柄 curl_ MULTI_ remove_手柄

  • 将CURLOPT_CONNECTTIMEOUT和CURLOPT_TIMEOUT设置为无穷大。

    • 更多。(我将更新这篇文章,因为我已经忘记了所有这些)

使用Php版本5.4.14进行测试 希望我已经很好地说明了这些要点。

感谢阅读。

1 个答案:

答案 0 :(得分:8)

我一直在尝试使用你的脚本一段时间试图让它发挥作用。
只有当我阅读Repeated calls to this function will return a new result each time, until a FALSE is returned as a signal that there is no more to get at this point.http://se2.php.net/manual/en/function.curl-multi-info-read.php时,才意识到while循环可能有用。

额外的while循环使其表现完全符合您的预期。这是我得到的输出:

http://smotri.com/video/list/sports/ failed

http://smotri.com/video/list/travel/ failed

http://smotri.com/video/list/gaming/ failed

http://smotri.com/video/list/erotic/ failed

http://smotri.com/video/list/humour/ failed

http://smotri.com/video/list/animals/ success

http://smotri.com/video/list/film/ success

http://smotri.com/video/list/auto/ success

http://smotri.com/video/list/ failed

http://smotri.com/video/list/hobby/ failed

http://smotri.com/video/list/mult/ failed

这是我用于测试的代码:

<?php
set_time_limit(0);

$l = array(
    'http://smotri.com/video/list/',
    'http://smotri.com/video/list/sports/',
    'http://smotri.com/video/list/animals/',
    'http://smotri.com/video/list/travel/',
    'http://smotri.com/video/list/hobby/',
    'http://smotri.com/video/list/gaming/',
    'http://smotri.com/video/list/mult/',
    'http://smotri.com/video/list/erotic/',
    'http://smotri.com/video/list/auto/',
    'http://smotri.com/video/list/humour/',
    'http://smotri.com/video/list/film/'
);

$mh = curl_multi_init();

$s = 0;
$f = 10;

while($s <= $f)
{   
    $ch = curl_init();  

    if($s%2)
    {
        $curlsettings = array(
            CURLOPT_URL => $l[$s],
            CURLOPT_TIMEOUT_MS => 3000,
            CURLOPT_RETURNTRANSFER => 1,
        );
    }
    else
    {
        $curlsettings = array(
            CURLOPT_URL => $l[$s],
            CURLOPT_TIMEOUT_MS => 4000,
            CURLOPT_RETURNTRANSFER => 1,
        );
    }

    curl_setopt_array($ch, $curlsettings);
    curl_multi_add_handle($mh,$ch);
    $s++;
}

$active = null;

do 
{

    $mrc = curl_multi_exec($mh,$active);
    curl_multi_select($mh);

    while($info = curl_multi_info_read($mh))
    {
        echo '<pre>';
        //var_dump($info);

        if($info['result'] === 0)
        {
            echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' success<br>';
        }
        else
        {
            echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' failed<br>';
        }   
    }

} while ($active > 0);

curl_multi_close($mh);

希望有所帮助。要进行测试,只需将CURLOPT_TIMEOUT_MS调整为您的互联网连接即可。我做到了这样它在3000到4000毫秒之间交替,因为3000将失败,4000通常会成功。

<强>更新

在浏览完PHP和libCurl文档之后,我发现curl_multi_exec是如何工作的(在libCurl中它的curl_multi_perform)。在第一次被调用时,它开始处理所有添加的句柄的传输(在通过curl_multi_add_handle之前添加)。

它分配的号码$ active是仍在运行的转移次数。因此,如果它小于您拥有的句柄总数,那么您就知道一个或多个传输已完成。所以curl_multi_exec也可以作为一种进度指示器。

由于所有传输都以非阻塞方式处理(传输可以同时完成),因此while循环curl_multi_exec的输入不能代表已完成的url请求的每次迭代。

所有数据都存储在队列中,因此只要完成一个或多个传输,您就可以调用curl_multi_info_read来获取此数据。

在我的原始答案中,我在while循环中使用了curl_multi_info_read。此循环将继续迭代,直到curl_multi_info_read在队列中找不到剩余数据。如果$ active!= 0(意味着curl_multi_exec报告传输仍未完成),则外部while循环将移至下一次迭代。

总而言之,当仍然没有完成传输时,外部循环继续迭代,并且内部循环仅在完成传输的数据时才进行迭代。

PHP文档对于curl多功能非常糟糕,所以我希望这清除了一些东西。下面是另一种做同样事情的方法。

do 
{
    curl_multi_exec($mh,$active);
} while ($active > 0);

// while($info = curl_multi_info_read($mh)) would work also here
for($i = 0; $i <= $f; $i++){
    $info = curl_multi_info_read($mh);

    if($info['result'] === 0)
    {
        echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' success<br>';
    }
    else
    {
        echo curl_getinfo($info['handle'],CURLINFO_EFFECTIVE_URL) . ' failed<br>';
    }
}


根据此信息,您还可以看到不需要curl_multi_select,因为您不希望在有活动之前阻止某些内容。

使用您在问题中提供的代码,似乎只有在几次传输失败后curl没有继续进行,但实际上仍有数据在缓冲区中排队。你的代码没有足够多次调用curl_multi_info_read。您的代码获取所有成功传输的原因是由于PHP在单个线程上运行,因此脚本挂起等待请求。失败请求的超时对PHP的影响不足以使其挂起/等待那么长时间,因此while循环执行的迭代次数少于排队数据的数量。