El módulo Parallel::ForkManager provee una API OOP
a la creación de procesos mediante fork
. La funcionalidad
adicional que se obtiene con el módulo es la posibilidad
de proveer una cota superior
al número de procesos que se pueden crear:
$pm = Parallel::ForkManager->new(5)
Una vez el objeto es creado es posible crear nuevos procesos
hijo mediante el método start
:
for my $link (@links) { $pm->start and next; get_link($link); $pm->finish; };
start
devuelve el pid
del hijo al proceso padre
y cero al hijo. La diferencia con un fork
nativo es que
start
bloquea al proceso padre si la cota superior
establecida en la llamada a new
es sobrepasada.
La llamada al método finish
termina el proceso
hijo.
Acepta un código opcional de salida.
El código de salida por defecto es cero.
La llamada por parte del padre al método
wait_all_children
(línea 30 del código que sigue)
hace que el padre
no continúe hasta que todos los procesos
hijo hayan terminado.
El código que sigue obtiene el nombre de un fichero
HTML
desde la línea de comandos.
La llamada HTML::TreeBuilder->new_from_file($rootdoc)
deja en $tree
el árbol de análisis sintáctico
del HTML. La llamada $tree->find_by_tag_name('a')
retorna todos los nodos ''ancla'' existentes en el árbol
(<a href"...">...</a>
). El map
de la línea 14
obtiene los atributos href
de esos nodos.
Mediante grep seleccionamos aquellas referencias a URL que sean completas.
El código de las líneas 23-30 se encarga de lanzar procesos concurrentes que descargan esos ficheros.
pp2@nereida:~/src/perl/forkmanager$ cat -n parforkaddr.pl 1 #!/usr/local/bin/perl -w 2 use strict; 3 use Carp; 4 use Parallel::ForkManager; 5 use LWP::Simple; 6 use HTML::TreeBuilder;El módulo LWP::simple provee facilidades para la creación y manejo de objetos que se comportan como navegadores o browsers.
El módulo HTML::TreeBuilder proporciona failidades para el análisis
sintáctico de fuentes HTML
.
8 my $debug = 1; 9 10 my $rootdoc = shift || die "Usage $0 html_file_name\n"; 11 12 my $tree = HTML::TreeBuilder->new_from_file($rootdoc);Suponemos que el guión se llama con un fichero
file.html
.
Los enlaces de la forma <a href="...">...</a>
serán descargados por el guión en nuestra máquina local.
La llamada a HTML::TreeBuilder->new_from_file
crea el árbol
de análisis sintáctico para el documento.
14 my @links = $tree->find_by_tag_name('a'); 15 @links = map { $_->attr("href") } @links; 16 @links = grep { defined and m{http://.*\.html?$} } @links; 17 { 18 local $" = "\n\t"; 19 print "Cargando:\n\t@links\n" if ($debug); 20 }La llamada a
find_by_tag_name
devuelve una lista con todos los
nodos ancla en el árbol.
La transformación mediante map
calcula los atributos href
de esos nodos.
22 my $pm = Parallel::ForkManager->new(5); 23 for my $link (@links) { 24 $pm->start and next; 25 26 get_link($link); 27 $pm->finish; 28 }; 29 30 $pm->wait_all_children; 31 32 sub get_link { 33 my $rootdoc = shift || croak "get_link error: provide a link\n"; 34 35 my ($fn)= $rootdoc =~ /^.*\/(.*?)$/; 36 warn "Cannot determine filename from $fn\n", return 1 unless $fn; 37 my $rc=getstore($rootdoc,$fn); 38 my $result = is_success($rc);La función
getstore
obtiene el documento y lo almacena.
El resultado de la petición queda en $rc
.
La función is_success
nos dice si la petición tuvo éxito.
39 if ($debug) { 40 my $prefix = $result? "$rootdoc downloaded": "Can't download $rootdoc"; 41 print "$prefix. Response code: $rc\n"; 42 } 43 return $result; 44 }
Casiano Rodríguez León