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
