Параллельные HTTP-запросы в PHP с использованием классов PECL HTTP [Ответ: класс HttpRequestPool]
Класс HttpRequestPool предоставляет решение. Большое спасибо тем, кто это указал.
Краткое руководство можно найти по адресу: http://www.phptutorial.info/?HttpRequestPool-construct
Проблема
Я хотел бы сделать параллельные/параллельные/одновременные HTTP-запросы в PHP. Я хотел бы избежать последовательных запросов как:
- набор запросов займет слишком много времени; тем больше запросов больше
- тайм-аут одного запроса на полпути через набор может привести к тому, что последующие запросы не будут выполнены (если script имеет ограничение времени выполнения)
Мне удалось найти детали для создания одновременных [sic] HTTP-запросов в PHP с помощью cURL, однако я бы хотел явно использовать PHP HTTP-функции, если это вообще возможно.
В частности, мне нужно отправлять данные POST одновременно с набором URL-адресов. URL-адреса, на которые распространяются данные, находятся вне моего контроля; они настроены пользователем.
Я не возражаю, если мне нужно дождаться завершения всех запросов до того, как ответы будут обработаны. Если я устанавливаю тайм-аут 30 секунд на каждый запрос, и запросы выполняются одновременно, я знаю, что я должен ждать максимум 30 секунд (возможно, немного больше) для всех запросов.
Я не могу найти подробностей о том, как это может быть достигнуто. Тем не менее, я недавно заметил упоминание в руководстве PHP о том, что PHP5 + может обрабатывать одновременные HTTP-запросы - я намеревался записать его в это время, забыл и не могу найти его снова.
Пример одиночного запроса (отлично работает)
<?php
$request_1 = new HttpRequest($url_1, HTTP_METH_POST);
$request_1->setRawPostData($dataSet_1);
$request_1->send();
?>
Пример параллельного запроса (неполный, ясно)
<?php
$request_1 = new HttpRequest($url_1, HTTP_METH_POST);
$request_1->setRawPostData($dataSet_1);
$request_2 = new HttpRequest($url_2, HTTP_METH_POST);
$request_2->setRawPostData($dataSet_2);
// ...
$request_N = new HttpRequest($url_N, HTTP_METH_POST);
$request_N->setRawPostData($dataSet_N);
// Do something to send() all requests at the same time
?>
Любые мысли были бы очень благодарны!
Разъяснение 1. Я хотел бы использовать функции HTTP PECL как:
- они предлагают приятный интерфейс OOP
- они широко используются в рассматриваемом приложении и придерживаются того, что уже используется, должно быть полезно с точки зрения обслуживания.
- Мне обычно приходится писать меньше строк кода, чтобы сделать HTTP-запрос с использованием функций PECL HTTP по сравнению с использованием cURL - меньшее количество строк кода также должно быть полезно с точки зрения обслуживания
Разъяснение 2: Я понимаю, что PHP-HTTP-функции не встроены и, возможно, я неправильно сформулировал то, что я исправлю. Я не беспокоюсь о том, что людям приходится устанавливать дополнительные вещи - это не приложение, которое должно быть распространено, это веб-приложение с сервером для себя.
Разъяснение 3. Я был бы очень доволен, если кто-то авторитетно заявит, что PECL HTTP не может этого сделать.
Ответы
Ответ 1
Я уверен, HttpRequestPool - это то, что вы ищете.
Чтобы разработать немного, вы можете использовать forking для достижения того, что вы ищете, но это кажется излишне сложным и не очень полезным в контексте HTML. Пока я не тестировал, этот код должен быть следующим:
// let $requests be an array of requests to send
$pool = new HttpRequestPool();
foreach ($requests as $request) {
$pool->attach($request);
}
$pool->send();
foreach ($pool as $request) {
// do stuff
}
Ответ 2
Вы пробовали HttpRequestPool (это часть Http)? Похоже, что он объединит объекты запроса и обработает их. Я знаю, что где-то я читал, что Http будет поддерживать одновременные запросы, и помимо пула я тоже ничего не могу найти.
Ответ 3
Мне когда-то приходилось решать аналогичную проблему: выполнять несколько запросов, не суммируя время отклика.
Это решение стало функцией пользовательской сборки, которая использовала неблокирующий sockets.
Он работает примерно так:
$request_list = array(
# address => http request string
#
'127.0.0.1' => "HTTP/1.1 GET /index.html\nServer: website.com\n\n",
'192.169.2.3' => "HTTP/1.1 POST /form.dat\nForm-data: ...",
);
foreach($request_list as $addr => $http_request) {
# first, create a socket and fire request to every host
$socklist[$addr] = socket_create();
socket_set_nonblock($socklist[$addr]); # Make operation asynchronious
if (! socket_connect($socklist[$addr], $addr, 80))
trigger_error("Cannot connect to remote address");
# the http header is send to this host
socket_send($socklist[$addr], $http_request, strlen($http_request), MSG_EOF);
}
$results = array();
foreach(array_keys($socklist) as $host_ip) {
# Now loop and read every socket until it is exhausted
$str = socket_read($socklist[$host_ip], 512, PHP_NORMAL_READ);
if ($str != "")
# add to previous string
$result[$host_ip] .= $str;
else
# Done reading this socket, close it
socket_close($socklist[$host_ip]);
}
# $results now contains an array with the full response (including http-headers)
# of every connected host.
Это намного быстрее, поскольку thunked reponses извлекается в полупараллельном, так как socket_read не ждет ответа, но возвращает, если сокет-буфер еще не заполнен.
Вы можете обернуть это в соответствующие интерфейсы ООП. Вам нужно будет создать строку HTTP-запроса самостоятельно и, конечно, обработать ответ сервера.
Ответ 4
Друг указал мне на CurlObjects (http://trac.curlobjects.com/trac), который я нашел весьма полезным для использования curl_multi.
$curlbase = new CurlBase;
$curlbase->defaultOptions[ CURLOPT_TIMEOUT ] = 30;
$curlbase->add( new HttpPost($url, array('name'=> 'value', 'a' => 'b')));
$curlbase->add( new HttpPost($url2, array('name'=> 'value', 'a' => 'b')));
$curlbase->add( new HttpPost($url3, array('name'=> 'value', 'a' => 'b')));
$curlbase->perform();
foreach($curlbase- > requests as $request) {
...
}
Ответ 5
PHP-функции HTTP тоже не построены - это расширение PECL. Если вы беспокоитесь о том, что людям приходится устанавливать дополнительные материалы, у обоих решений будет такая же проблема - и cURL, скорее всего, будет установлен, как я полагаю, поскольку он приходит по умолчанию с каждым веб-хостом, на котором я когда-либо был.
Ответ 6
Вы можете использовать pcntl_fork() для создания отдельного процесса для каждого запроса, а затем дождаться окончания:
http://www.php.net/manual/en/function.pcntl-fork.php
Есть ли причина, по которой вы не хотите использовать cURL? Функции curl_multi_ * позволят одновременно выполнять несколько запросов.