Возможно ли выполнение асинхронных HTTP-запросов с помощью PHP?
У меня есть сценарий PHP, который должен загрузить несколько файлов с удаленного сервера. На данный момент у меня просто есть цикл загрузки и обработки файлов с помощью cURL, что означает, что он не начинает загружать один файл, пока не завершится предыдущий - это значительно увеличивает время выполнения скрипта.
Можно ли запустить несколько экземпляров cURL, например, для асинхронной загрузки этих файлов одновременно, не дожидаясь завершения предыдущего? Если так, как это будет достигнуто?
5 ответов
Да.
Существует многозапрошенная PHP-библиотека (или см.: архивированный проект Google Code). Это многопоточная библиотека CURL.
В качестве другого решения вы можете написать скрипт, который делает это на языке, поддерживающем многопоточность, таком как Ruby или Python. Затем просто вызовите скрипт с помощью PHP. Кажется довольно просто.
Проверить скручиваемость легко. Он поддерживает как блокирующие, так и неблокирующие запросы одновременно или один запрос. Кроме того, он тестируется модульно, в отличие от многих простых или с ошибками библиотек.
Раскрытие: я являюсь автором этой библиотеки. У библиотеки есть собственный набор тестов, поэтому я уверен, что он надежный.
Также, посмотрите пример использования ниже:
<?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();
Для PHP5.5 +, mpyw / co является окончательным решением. Это работает, как будто это TJ / Co в JavaScript.
пример
Предположим, что вы хотите загрузить указанные аватары нескольких пользователей GitHub. Следующие шаги необходимы для каждого пользователя.
- Получить содержание http://github.com/mpyw (GET HTML)
- найти
<img class="avatar" src="...">
и запросить его (ПОЛУЧИТЬ ИЗОБРАЖЕНИЕ)
---
: Жду моего ответа...
: Ожидание другого ответа в параллельных потоках
Многие известные 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. Для получения более подробной информации посетите страницу хранилища.
Библиотека @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 />
* e.g. 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 e.g.<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;
}
В 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 &" в конце вашей команды может помочь, просто не забывайте правильно оборачивать вашу команду.