miércoles, 19 de enero de 2011

Múltiples peticiones HTTP simultaneas con PHP

Ante el problema de tener que leer mucha información de varios sitios de forma simultanea, como podría ser la carga de varios feeds, carga de comentarios, lista de resultados... Normal mente en casos se usa file_get_contents(), es un método sincronizado, pero hay que esperar a que finalice para ejecutarlo nuevamente.
Si queremos no esperar a que termine curl_multi_* permite   hacer muchas llamadas URL en muy poco tiempo, no es sincronizado pero tampoco muy paralelo que podamos decir.
Veamos un ejemplo. El tiempo total de las peticiones "lentas" seria:
  • URL 1: 2,3 segundos
  • URL 2: 0,3 segundos
  • URL 3: 1,4 segundos
  • URL 4: 1,8 segundos
  • URL 5: 3,0 segundos
Se tardaría 8,8 segundos sin sumarle tiempo de conexión lenta o desconexión o carga de otros elementos.

Como optimizarlo:
<?php
function PeticionMultiple($urls, $opciones = array()) {
    $curly = array();
    $resultado = array();
    $pm = curl_multi_init();
    foreach ($urls as $id => $d) {
        $curly[$id] = curl_init();    foreach ($urls as $id => $d) {

        $curly[$id] = curl_init();
        if(is_array($d) && !empty($d['url'])) {
            $url = $d['url']
        } else {
            $url = $d;
        }
        curl_setopt($curly[$id], CURLOPT_URL, $url);
        curl_setopt($curly[$id], CURLOPT_HEADER, 0);
        curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, 1);
        if (!empty($d['post'])) {
            curl_setopt($curly[$id], CURLOPT_POST, 1);
            curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $d['post']);
        }
        if (!empty($opciones)) {
            curl_setopt_array($curly[$id], $opciones);
        }
        curl_multi_add_handle($pm, $curly[$id]);
    }
    $ejecutando = null;
    do {
        curl_multi_exec($pm, $ejecutando);
    } while($ejecutando > 0);
        foreach($curly as $id => $c) {
            $resultado[$id] = curl_multi_getcontent($c);
            curl_multi_remove_handle($pm, $c);
        }
    curl_multi_close($pm);
    return $resultado;
}
?>

Para ejecutarlo usaríamos lo siguiente:
<?php
    $urls = array(
        'http://javiercasares.com/feed/atom/',
        'http://www.ethek.com/feed/atom/',
        'http://www.tumanitas.com/feed/'
    );
    $r = PeticionMultiple($urls);
    print_r($r);
?>

Ya tendríamos múltiples peticiones para leer feeds de diferentes sitios lo más rápido posible.
Fuente: github

5 comentarios:

  1. una pregunta infinitas ejecuciones del curl a un web site, esto traeria consecuencia en tumbarlo dicha web ??? es la duda q tengo.

    ResponderEliminar
  2. estas en lo cierto, si tu sitio web/servidor/equipo realiza infinitas peticiones dirigidas a otra web se considera como un ataque DDOS. Pero también las consecuencias que tiene para tu servidor o donde lo tengas alojado son grandes por que estaría trabajando tanto que posiblemente el que se bloquea es tu sitio web/servidor/equipo. Si estas interesado en eso hay técnicas como la del uso de array que ayudan a que no sufra una caída el atacante.

    ResponderEliminar
  3. a eso me referia, ataque DDOS, pero q pasa si ejecuto curl a un web site desde local... igual lo tumba?

    ResponderEliminar
  4. para ejecutarlo así haces lo mismo que un ping.

    ResponderEliminar
  5. aya , gracias por tu respuesta :D

    ResponderEliminar