【问题标题】:PHP curl_multi_getcontent returns nullPHP curl_multi_getcontent 返回 null
【发布时间】:2013-09-13 23:26:20
【问题描述】:

我一直在关注如何使用curl_multi 的教程。 http://arguments.callee.info/2010/02/21/multiple-curl-requests-with-php/

我不知道我做错了什么,但 curl_multi_getcontent 返回 null。假设返回 JSON。我知道这不是 mysql 调用,因为我使用 while 循环和标准 curl_exec 工作,但页面加载时间太长。 (为了安全起见,我更改了一些 setopt 细节)

相关PHP代码sn-p。最后我确实关闭了while循环。

$i = 0;
$ch = array();
$mh = curl_multi_init();
while($row = $result->fetch_object()){
   $ch[$i] = curl_init();
   curl_setopt($ch[$i], CURLOPT_CAINFO, 'cacert.pem');
   curl_setopt($ch[$i], CURLOPT_USERPWD, "$username:$password");
   curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true); 
   curl_setopt($ch[$i], CURLOPT_URL, 'https://mysite.com/search/'.$row->username.'/');
   curl_multi_add_handle($mh, $ch[$i]);
   $i++;
}
$running = 0;
do {
    curl_multi_exec($mh, $running);
} while ($running > 0);
$result->data_seek(0);
$i = 0;
while ($row = $result->fetch_object()) {
    $data = curl_multi_getcontent($ch[$i]);
    $json_data = json_decode($data);
    var_dump($json_data);

编辑

这是当前可以运行的代码,但会导致页面加载太慢

$ch = curl_init();
curl_setopt($ch, CURLOPT_CAINFO, 'cacert.pem');
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 
while($row = $result->fetch_object()){
   curl_setopt($ch, CURLOPT_URL, 'https://mysite.com/search/'.$row->username.'/');
   $data = curl_exec($ch);
   $json_data = json_decode($data);
   var_dump($json_data);
}

【问题讨论】:

  • CURLOPT_USERPWD 假设您在网站上有 http 密码验证。如果以这种方式设置,请将代码更改为 curl_setopt($ch[$i], CURLOPT_USERPWD, "$username:$password");用双引号。
  • 你有没有 var_dumped $ch[$i] 看看它是否包含它应该包含的内容?
  • @velcrow 刚刚在您发表评论之前就注意到了这个错误。我以为它会解决它,但它没有。 :(
  • 还可以尝试将var_dump(curl_error($ch[$i]));curl_errno 结合使用
  • 好吧,这很奇怪。 curl 库、SSL 和curl_multi_init 过去曾出现过错误,但这些通常是内存泄漏。我认为CURLOPT_CAINFO 选项不是问题,而是 curl 库无法协商 SSL 协议。使用CURLOPT_SSL_CIPHER_LIST 分配密码可能会有所帮助,但不能保证。此外,请仔细检查 CURLOPT_URL 中发送的 URL,如果您的 DNS 提供商将失败的 DNS 请求重定向到另一台主机,则可能会导致此错误。

标签: php curl curl-multi


【解决方案1】:

我想知道:

$i = 0;
while ($row = $result->fetch_object()) {
    $data = curl_multi_getcontent($ch[$i]);
    $json_data = json_decode($data);
    var_dump($json_data);

你忘记增加 $i 了吗?如果是这样,您已经获取了 $ch[0] 的内容,然后您再次调用 curl_multi_getcontent。

另外,我写了一篇关于concurrent requests with PHP's cURL extension 的博文,其中包含一个用于 curl 多请求的通用函数。您可以通过以下方式调用此函数:

$responses = multi([
    $requests = [
        ['url' => 'https://example.com/search/username1/'],
        ['url' => 'https://example.com/search/username2/'],
        ['url' => 'https://example.com/search/username3/']
    ]
    $opts = [
        CURLOPT_CAINFO => 'cacert.pem',
        CURLOPT_USERPWD => "username:password"
    ]
]);

然后,循环遍历响应数组:

foreach ($responses as $response) {
    if ($response['error']) {
        // handle error
        continue;
    }
    // check for empty response
    if ($response['data'] === null) {
        // examine $response['info']
        continue;
    }
    // handle data
    $data = json_decode($response['data']);
    // do something
}

使用此函数,您可以使用以下调用进行访问 https 站点的简单测试:

multi(
    $requests = [
        'google' => ['url' => 'https://www.google.com'],
        'linkedin' => ['url'=> 'https://www.linkedin.com/']
    ],
    $opts = [
        CURLOPT_CAINFO => '/path/to/your/cacert.pem',
        CURLOPT_SSL_VERIFYPEER => true
    ]
);

【讨论】:

  • 我确实增加了 $i。问题是Unknown SSL protocol error 你知道为什么curl_setopt($ch[$i], CURLOPT_CAINFO, 'cacert.pem'); 不能与curl_multi_exec 一起使用,但可以与常规curl_exec 一起使用
  • 我已经为函数添加了另一个示例调用。这适用于我的系统。您能否尝试使用博客文章中的功能并在您的系统上进行此调用。这是一个非常基本的测试,只需同时抓取两个 https 站点(google 和linkedin)。如果可行,我们可以从那里开始工作。
  • 这是迄今为止发布的最佳答案。
  • @BlakePlumb 你能试试这个,看看你的设置发生了什么吗?该函数还返回请求的信息,这也很有用。
【解决方案2】:

我发现您的执行循环与PHP documentation 中建议的循环不同:

do {
  $mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);

请注意,在while 中比较的是函数返回,而不是第二个参数。

编辑: 感谢 Adam 的评论,我测试了这两种语法,发现它们是平等的和异步的。 这是一个将内容放入变量的异步多请求的工作示例:

<?php
$ch = array();
$mh = curl_multi_init();
$total = 100;

echo 'Start: ' . microtime(true) . "\n";

for ($i = 0; $i < $total; $i++) {
    $ch[$i] = curl_init();
    curl_setopt($ch[$i], CURLOPT_URL, 'http://localhost/sleep.php?t=' . $i);
    curl_setopt($ch[$i], CURLOPT_HEADER, 0);
    curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, true);

    curl_multi_add_handle($mh, $ch[$i]);
}

$active = null;
do {
    $mrc = curl_multi_exec($mh, $active);
    usleep(100); // Maybe needed to limit CPU load (See P.S.)
} while ($active);

foreach ($ch AS $i => $c) {
    $r = curl_multi_getcontent($c);
    var_dump($r);
    curl_multi_remove_handle($mh, $c);
}

curl_multi_close($mh);

echo 'End: ' . microtime(true) . "\n";

并测试文件 sleep.php:

<?php
$start = microtime(true);

sleep( rand(3, 5) );

$end = microtime(true);

echo $_GET['t'], ': ', $start, ' - ', $end, ' - ', ($end - $start);
echo "\n";

附:在循环中使用 usleep 的最初想法是暂停它一点,从而减少 cUrl 等待响应时的操作数量。一开始它似乎是这样工作的。但最近使用top 进行的测试显示 CPU 负载差异很小(usleep 为 17%,而没有它为 20%)。所以,不知道要不要用。也许在真实服务器上的测试会显示另一个结果。

编辑 2:我已经通过向受密码保护的 HTTPS 页面发出请求来测试我的代码(CURLOPT_CAINFOCURLOPT_USERPWD 等于问题中的那些)。它按预期工作。您的 PHP 或 cURL 版本中可能存在错误。我的版本是“PHP 版本 5.3.10-1ubuntu3.8”和 7.22.0。他们没有问题。

【讨论】:

  • 您可以使用多种方式处理 curl_multi* 函数。最简单的方法是等待所有请求完成,然后一次性处理所有响应,这就是他的示例所做的。
【解决方案3】:

您是否将 CURLOPT_SSL_VERIFYPEER 设置为 true?

【讨论】:

  • 默认设置为true。
【解决方案4】:

使用$running = null; 代替$running = 0;

根据链接:

  1. multiple-curl-requests-with-php

  2. http://www.php.net/manual/en/function.curl-multi-exec.php

在这两种情况下,变量都被定义为NULL,这是因为

curl_multi_exec ( resource $mh , int &$still_running )

第二个参数是对变量的引用。

另外,您可能会发现这很有用:php single curl works but multi curl doesn't work

【讨论】:

    【解决方案5】:

    curl_multi_exec 执行多线程 HTTP 请求,并且请求可能不是按照您将它们添加到 multihandler$mh 的顺序完成的。要获得已完成请求的响应,您应该使用 curl_multi_info_read 函数。您可以在 php.net http://php.net/manual/ru/function.curl-multi-info-read.php 上阅读更多相关信息

    【讨论】:

    • 顺序应该不是问题,因为他使用特定句柄来访问每个响应。
    猜你喜欢
    • 2014-03-27
    • 2015-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 2019-10-04
    • 2015-10-11
    • 2012-05-03
    相关资源
    最近更新 更多