threads - Solicitudes HTTP paralelas en PHP utilizando clases HTTP PECL
php threads tutorial (6)
¿Has probado HttpRequestPool (es parte de Http)? Parece que agruparía los objetos de solicitud y los trabajaría. Sé que leí en algún lado que Http admitiría solicitudes simultáneas y, aparte del pool , tampoco puedo encontrar nada.
La clase HttpRequestPool proporciona una solución. Muchas gracias a los que señalaron esto.
Un breve tutorial se puede encontrar en: http://www.phptutorial.info/?HttpRequestPool-construct
Problema
Me gustaría hacer solicitudes HTTP concurrentes / paralelas / simultáneas en PHP. Me gustaría evitar solicitudes consecutivas como:
- un conjunto de solicitudes tomará demasiado tiempo para completarse; cuantas más solicitudes, más
- el tiempo de espera de una solicitud a la mitad de un conjunto puede hacer que no se realicen solicitudes posteriores (si una secuencia de comandos tiene un límite de tiempo de ejecución)
He logrado encontrar detalles para hacer solicitudes HTTP simultáneas [sic] en PHP con cURL ; sin embargo, me gustaría utilizar explícitamente las funciones HTTP de PHP si es posible.
Específicamente, necesito POSTAR datos simultáneamente a un conjunto de URL. Las URL a las que se envían los datos están fuera de mi control; están establecidos por el usuario.
No me importa si tengo que esperar a que finalicen todas las solicitudes para poder procesar las respuestas. Si establezco un tiempo de espera de 30 segundos en cada solicitud y las solicitudes se realizan al mismo tiempo, sé que debo esperar un máximo de 30 segundos (quizás un poco más) para que se completen todas las solicitudes.
No puedo encontrar detalles de cómo se puede lograr esto. Sin embargo, hace poco noté una mención en el manual de PHP de PHP5 + que me permitía manejar solicitudes HTTP simultáneas; tenía la intención de anotarlo en ese momento, lo olvidé y no puedo encontrarlo nuevamente.
Ejemplo de solicitud individual (funciona bien)
<?php
$request_1 = new HttpRequest($url_1, HTTP_METH_POST);
$request_1->setRawPostData($dataSet_1);
$request_1->send();
?>
Ejemplo de solicitud concurrente (incompleto, claramente)
<?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
?>
¡Cualquier idea sería muy apreciada!
Aclaración 1 : Me gustaría apegarme a las funciones HTTP PECL como:
- Ofrecen una agradable interfaz OOP
- se usan ampliamente en la aplicación en cuestión y apegarse a lo que ya está en uso debería ser beneficioso desde una perspectiva de mantenimiento
- En general, tengo que escribir menos líneas de código para hacer una solicitud HTTP utilizando las funciones PECL HTTP en comparación con el uso de cURL: menos líneas de código también deberían ser beneficiosas desde una perspectiva de mantenimiento
Aclaración 2 : me doy cuenta de que las funciones HTTP de PHP no están integradas y quizás redacté las cosas erróneamente allí, lo cual corregiré. No me preocupa que las personas tengan que instalar cosas adicionales; esta no es una aplicación que se distribuirá, es una aplicación web con un servidor para sí misma.
Aclaración 3 : Sería perfectamente feliz si alguien afirma con autoridad que el HTTP PECL no puede hacer esto.
Las funciones HTTP de PHP tampoco están integradas , son una extensión PECL. Si su preocupación es que la gente tenga que instalar cosas adicionales, ambas soluciones tendrán el mismo problema, y es más probable que se instale cURL, supongo, ya que viene con todos los servidores web en los que he estado.
Puede usar pcntl_fork () para crear un proceso separado para cada solicitud, luego espere a que finalicen:
http://www.php.net/manual/en/function.pcntl-fork.php
¿Hay alguna razón por la que no quieras usar cURL? Las funciones curl_multi_ * permitirían múltiples solicitudes al mismo tiempo.
Estoy bastante seguro de que HttpRequestPool es lo que estás buscando.
Para elaborar un poco, puede usar bifurcación para lograr lo que está buscando, pero eso parece innecesariamente complejo y poco útil en un contexto HTML. Si bien no lo he probado, este código debería ser:
// 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 }
Una vez tuve que resolver un problema similar: hacer varias solicitudes sin acumular los tiempos de respuesta.
La solución terminó siendo una función de creación personalizada que utilizaba sockets no bloqueantes. Funciona de la siguiente manera:
$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.
Es mucho más rápido ya que las respuestas thunk se obtienen en semi-paralelo ya que socket_read no espera la respuesta sino que retorna si el socket-buffer aún no está lleno.
Puede envolver esto en las interfaces OOP apropiadas. Tendrá que crear la cadena de solicitud HTTP usted mismo y procesar la respuesta del servidor, por supuesto.
Un amigo me señaló CurlObjects ( http://trac.curlobjects.com/trac ) recientemente, que encontré bastante útil para usar 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-> solicitudes como $ solicitud) {...}