PHP curl_multi_getcontent возвращает null
Я следую этому руководству о том, как использовать 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. Я закрываю цикл 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);
}
Ответы
Ответ 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.
Кроме того, я написал пост в блоге, охватывающий параллельные запросы с расширением PHP 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
]
);
Ответ 2
Я вижу, что ваш цикл выполнения отличается от того, который рекомендуется в документации PHP:
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
Обратите внимание, что в while
сравнивается функция return, а не второй параметр.
Edit:
Благодаря комментарию Адама я тестировал оба синтаксиса и вижу, что они равны и асинхронны.
Ниже приведен рабочий пример асинхронного многопротокола с получением содержимого в переменную:
<?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";
P.S. Первоначальная идея использования usleep
внутри цикла заключалась в том, чтобы немного приостановить его и, следовательно, уменьшить количество операций, в то время как cUrl ожидает ответа. И вначале это работало именно так. Но последние тесты с top
показали минимальную разницу в загрузке процессора (17% с usleep
против 20% без него). Поэтому я не знаю, использовать это или нет. Возможно, тесты на реальном сервере покажут другие результаты.
Изменить 2. Я проверил свой код с запросом на защищенную паролем страницу HTTPS (CURLOPT_CAINFO
и CURLOPT_USERPWD
, равную тем, что указаны в вопросе). Он работает так, как ожидалось. Вероятно, есть ошибка в вашей версии PHP или cURL. Мои версии: "Версия PHP 5.3.10-1ubuntu3.8" и 7.22.0. У них нет проблем.
Ответ 3
Используйте $running = null;
вместо $running = 0;
.
По ссылкам:
В обоих случаях переменная была определена как NULL, это потому, что
curl_multi_exec ( resource $mh , int &$still_running )
Второй аргумент - это ссылка на переменную.
Кроме того, вы можете найти это полезным: php single curl works, но multi curl не работает
Ответ 4
Установили ли вы CURLOPT_SSL_VERIFYPEER значение true?
Ответ 5
curl_multi_exec
выполняет многозадачные HTTP-запросы и запросы, которые могут быть выполнены не для того, чтобы вы добавили их в multihandler $mh
. Чтобы получить ответ от выполненных запросов, вы должны использовать функцию curl_multi_info_read
. Подробнее об этом читайте на php.net http://php.net/manual/ru/function.curl-multi-info-read.php