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