У меня есть PHP-скрипт, которому необходимо загрузить несколько файлов с удаленного сервера. На данный момент у меня просто есть цикл загрузки и обработки файлов с помощью cURL, а это означает, что он не запускает загрузку одного файла до тех пор, пока предыдущий не будет завершен – это значительно увеличивает время выполнения скрипта.
Можно ли запустить несколько экземпляров cURL, например, для асинхронной загрузки этих файлов одновременно, не дожидаясь завершения предыдущего? Если да, то как это будет достигнуто?
Да.
Существует многострочная библиотека PHP (или см.: Проект Google Code ). Это многопоточная библиотека CURL.
В качестве другого решения вы можете написать сценарий, который делает это на языке, который поддерживает потоки, такие как Ruby или Python. Затем просто вызовите скрипт с PHP. Кажется довольно простым.
Проверьте curl-easy . Он поддерживает как блокирующие, так и неблокирующие запросы одновременно или по одному запросу. Кроме того, он протестирован с помощью модуля, в отличие от многих простых или багги-библиотек.
Раскрытие информации: Я являюсь автором этой библиотеки. В библиотеке есть собственный набор тестов, поэтому я уверен, что он надежный.
Кроме того, посмотрите пример использования ниже:
<?php // We will download info about 2 YouTube videos: // http://youtu.be/XmSdTa9kaiQ and // http://youtu.be/6dC-sm5SWiU // Init queue of requests $queue = new cURL\RequestsQueue; // Set default options for all requests in queue $queue->getDefaultOptions() ->set(CURLOPT_TIMEOUT, 5) ->set(CURLOPT_RETURNTRANSFER, true); // Set callback function to be executed when request will be completed $queue->addListener('complete', function (cURL\Event $event) { $response = $event->response; $json = $response->getContent(); // Returns content of response $feed = json_decode($json, true); echo $feed['entry']['title']['$t'] . "\n"; }); $request = new cURL\Request('http://gdata.youtube.com/feeds/api/videos/XmSdTa9kaiQ?v=2&alt=json'); $queue->attach($request); $request = new cURL\Request('http://gdata.youtube.com/feeds/api/videos/6dC-sm5SWiU?v=2&alt=json'); $queue->attach($request); // Execute queue $queue->send();
Библиотека @stil – это так здорово. Огромное спасибо ему!
Тем не менее, я написал хорошую функцию полезности, которая позволяет легко получать асинхронное содержимое из нескольких URL-адресов (API в моем случае) и возвращать их, не теряя информацию, которая есть.
Вы просто запускаете его, передавая ключ => массив значений в качестве ввода, и он возвращает массив ключей => ответ : -)
/** * This function runs multiple GET requests parallely.<br /> * @param array $urlsArray needs to be in format:<br /> * <i>array(<br /> * [url_unique_id_1] => [url_for_request_1],<br /> * [url_unique_id_2] => [url_for_request_2],<br /> * [url_unique_id_3] => [url_for_request_3]<br /> * )</i><br /> * eg input like:<br /> * <i>array(<br /> * "myemail@gmail.com" => * "http://someapi.com/results?search=easylife",<br /> * "michael@gmail.com" => * "http://someapi.com/results?search=safelife"<br /> * )</i> * @return array An array where for every <i>url_unique_id</i> response to this request * is returned eg<br /> * <i>array(<br /> * "myemail@gmail.com" => <br /> * "Work less, enjoy more",<br /> * "michael@gmail.com" => <br /> * "Study, work, pay taxes"<br /> * )</i> * */ public function getResponsesFromUrlsAsynchronously(array $urlsArray, $timeout = 8) { $queue = new \cURL\RequestsQueue; // Set default options for all requests in queue $queue->getDefaultOptions() ->set(CURLOPT_TIMEOUT, $timeout) ->set(CURLOPT_RETURNTRANSFER, true); // ========================================================================= // Define some extra variables to be used in callback global $requestUidToUserUrlIdentifiers; $requestUidToUserUrlIdentifiers = array(); global $userIdentifiersToResponses; $userIdentifiersToResponses = array(); // ========================================================================= // Set function to be executed when request will be completed $queue->addListener('complete', function (\cURL\Event $event) { // Define user identifier for this url global $requestUidToUserUrlIdentifiers; $requestId = $event->request->getUID(); $userIdentifier = $requestUidToUserUrlIdentifiers[$requestId]; // ========================================================================= $response = $event->response; $json = $response->getContent(); // Returns content of response $apiResponseAsArray = json_decode($json, true); $apiResponseAsArray = $apiResponseAsArray['jobs']; // ========================================================================= // Store this response in proper structure global $userIdentifiersToResponses; $userIdentifiersToResponses[$userIdentifier] = $apiResponseAsArray; }); // ========================================================================= // Add all request to queue foreach ($urlsArray as $userUrlIdentifier => $url) { $request = new \cURL\Request($url); $requestUidToUserUrlIdentifiers[$request->getUID()] = $userUrlIdentifier; $queue->attach($request); } // ========================================================================= // Execute queue $queue->send(); // ========================================================================= return $userIdentifiersToResponses; }
Для PHP5.5 + mpyw / co является окончательным решением. Он работает так, как если бы он был tj / co в JavaScript.
Предположим, что вы хотите загрузить указанные несколько аватаров пользователей GitHub. Для каждого пользователя требуются следующие шаги.
<img class="avatar" src="...">
и запросите его (GET IMAGE) ---
: Ожидание ответа
...
: Ожидание другого ответа в параллельных потоках
Многие известные curl_multi
основе curl_multi
уже предоставляют нам следующие потоки.
/-----------GET HTML\ /--GET IMAGE.........\ / \/ \ [Start] GET HTML..............----------------GET IMAGE [Finish] \ /\ / \-----GET HTML....../ \-----GET IMAGE....../
Однако это недостаточно эффективно. Вы хотите уменьшить бесполезное время ожидания ...
?
/-----------GET HTML--GET IMAGE\ / \ [Start] GET HTML----------------GET IMAGE [Finish] \ / \-----GET HTML-----GET IMAGE.../
Да, это очень легко с mpyw / co. Для получения дополнительной информации перейдите на страницу репозитория.
В PHP 7.0 и Apache 2.0, как сказано в PHP exec Document , перенаправление вывода, добавив « &> / dev / null & » в конце команды, может заставить его работать на фоне, просто не забудьте правильно выполнить команду ,
$time = microtime(true); $command = '/usr/bin/curl -H \'Content-Type: application/json\' -d \'' . $curlPost . '\' --url \'' . $wholeUrl . '\' >> /dev/shm/request.log 2> /dev/null &'; exec($command); echo (microtime(true) - $time) * 1000 . ' ms';
Выше работает хорошо для меня, занимает всего 3 мс, но следующий не будет работать, занимает 1500 мс.
$time = microtime(true); $command = '/usr/bin/curl -H \'Content-Type: application/json\' -d \'' . $curlPost . '\' --url ' . $wholeUrl; exec($command . ' >> /dev/shm/request.log 2> /dev/null &'); echo (microtime(true) - $time) * 1000 . ' ms';
В общем, добавление «&> / dev / null &» в конце вашей команды может помочь, просто запомните правильную WRAP свою команду.