Синхронные HTTP запросы в php, используя cURL
Опубликовано TermiT марта 14, 2008 в PHP |
Перевод заметки Simultaneuos HTTP requests in PHP with cURL с блога phpied.com
Основная идея Веб 2.0 машапов получение данных от стороннего сервиса или сервисов и обработка их результатов представленная в интересном ключе. Это означает, что вам придется отправлять большое количество HTTP запросов к этому сервису или сервисам. Если вы воспользуетесь PHP функцией file_get_contents (), то запросы будут выполняться не синхронно, а поочерёдно, то есть пока не будут получены данные от первого запроса второй запрос не будет выполнятся. А если вам нужно выполнить три запроса и каждому из них необходима одна секунда на исполнение, то ваше приложение «задумается», по меньшей мере, на три секунды.
Решение
Конечно, можно и нужно использовать кэширование запросов, но первоначально запросы все же нужно делать.
Используя семейство curl_multi* cURL-функций можно достичь синхронного выполнения запросов. В этом случае ваше приложение «задумается» на промежуток равный затраченному времени на самый трудоемкий запрос, в противовес сумме времени всех запросов.
Реализация
Представляю вам свою функцию которая позволит вам выполнять запросы синхронно.
-
<?php
-
-
-
// массив curl дескрипторов
-
// массив с возвращенными данными
-
-
// инициализация многосложного curl дескриптора
-
$mh = curl_multi_init();
-
-
// цикл по элементам массива $data:
-
// инициализация простых curl дескрипторов
-
// и добавление их к многосложному curl дескриптору
-
foreach ($data as $id => $d) {
-
-
$curly[$id] = curl_init();
-
-
curl_setopt($curly[$id], CURLOPT_URL, $url);
-
curl_setopt($curly[$id], CURLOPT_HEADER, 0);
-
curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, 1);
-
-
// определяем тип передачи параметров в запросе
-
// GET или POST
-
curl_setopt($curly[$id], CURLOPT_POST, 1);
-
curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $d['post']);
-
}
-
}
-
-
// указываем дополнительные опции, если нужно
-
curl_setopt_array($curly[$id], $options);
-
}
-
-
curl_multi_add_handle($mh, $curly[$id]);
-
}
-
-
// выполняем запрос
-
$running = null;
-
do {
-
curl_multi_exec($mh, $running);
-
} while($running> 0);
-
-
// получаем данные и уничтожаем дискриптор
-
foreach($curly as $id => $c) {
-
$result[$id] = curl_multi_getcontent($c);
-
curl_multi_remove_handle($mh, $c);
-
}
-
-
// закрываем многосложный дескриптор
-
curl_multi_close($mh);
-
-
return $result;
-
}
-
-
?>
Итог
Функция принимает в качестве аргументов массив URL’ов и опционально дополнительный параметры cURL опций. Первый массив может иметь простую численную индексацию или это может быть массивом массивов, где второй ключ должен иметь имя «url». Если вы пользуетесь вторым способом, вы также можете добавить ключ «post», тогда данные будут передаваться методом POST.
Функция возвращает массив строк содержащих результат запроса (ответ сервиса) с той же индексацией, что и в массиве с параметрами запроса.
GET пример
Допустим вы решили воспользоваться сервисом поиска от Yahoo (документация на YDN) для создания машап сервиса энциклопедии музыкальных исполнителей. Выполнением следующего кода вы получите результаты поиска по аудио, видео и изображениям одновременно:
-
<?php
-
-
'http://search.yahooapis.com/VideoSearchService/V1/videoSearch?appid=YahooDemo&query=Pearl+Jam&output=json',
-
'http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=Pearl+Jam&output=json',
-
'http://search.yahooapis.com/AudioSearchService/V1/artistSearch?appid=YahooDemo&artist=Pearl+Jam&output=json'
-
);
-
$r = multiRequest($data);
-
-
echo '<pre>';
-
-
?>
В результате вы получите, что-то похожее на это:
-
(
-
[0] => {«ResultSet»:{«totalResultsAvailable»:«633»,«totalResultsReturned»:...
-
[1] => {«ResultSet»:{«totalResultsAvailable»:«105342»,«totalResultsReturned»:...
-
[2] => {«ResultSet»:{«totalResultsAvailable»:10,«totalResultsReturned»:...
-
)
POST пример
Yahoo предлагает api к очень интересному сервису: term extraction, который анализирует большие куски текста и выдает релевантные фразы. Вот пример выполнения одновременно двух запросов к этому сервису использую POST.
-
<?php
-
-
$data[0]['url'] = 'http://search.yahooapis.com/ContentAnalysisService/V1/termExtraction';
-
$data[0]['post']['appid'] = 'YahooDemo';
-
$data[0]['post']['output'] = 'php';
-
$data[0]['post']['context'] = 'Now I lay me down to sleep,
-
I pray the Lord my soul to keep;
-
And if I die before I wake,
-
I pray the Lord my soul to take.';
-
-
$data[1]['url'] = 'http://search.yahooapis.com/ContentAnalysisService/V1/termExtraction';
-
$data[1]['post']['appid'] = 'YahooDemo';
-
$data[1]['post']['output'] = 'php';
-
$data[1]['post']['context'] = 'Now I lay me down to sleep,
-
I pray the funk will make me freak;
-
If I should die before I waked,
-
Allow me Lord to rock out naked.';
-
-
$r = multiRequest($data);
-
-
?>
И результат:
-
(
-
[0] => a:1:{s:9:«ResultSet»;a:1:{s:6:«Result»;s:5:«sleep»;}}
-
[1] => a:1:{s:9:«ResultSet»;a:1:{s:6:«Result»;a:3:{i:0;s:5:«freak»;i:1;s:5:«sleep»;i:2;s:4:«funk»;}}}
-
)
От себя добавлю: я встречался с подобной проблемой, когда занимался разработкой системы для одного литературного агентства. Система занималась тем, что разбивала текст на короткие части и отправляла их поисковой машине. Эта была своеобразная система проверки авторов на плагиат. Решил я проблему, не так элегантно как автор данной заметки, а простым выносом той части кода, что отвечала за составление и обработку результатов запроса в отдельный скрипт, который запускал из основного приложения с помощью exec (), которой передавалась команда с амперсандом на конце.
Далее