Программист решил усовершенствовать скрипт для скачивания веб-страниц из задачи “Скачать все”, и добавить в него параллельности с помощью модуля Parallel::ForkManager.
Итак, программист взял пример из документации, добавил логгирование, получилось примерно так:
#!/usr/bin/perl
use strict;
use warnings;
use Log::Log4perl;
use LWP::Simple;
use Parallel::ForkManager;
# links to download
my @links = (
["http://www.cpan.org/misc/images/cpan.png", "cpan-logo.png"],
["https://metacpan.org/static/images/logo.png", "metacpan-logo.png"],
["http://yandex.st/www/1.817/yaru/i/logo.png", "yandex-logo.png"],
["https://www.google.ru/images/srpr/logo11w.png", "google-logo.png"],
["https://mmedia2.ozone.ru/graphics/ozon/logo_220_87.png", "ozon-logo.png"],
["http://www.foo.bar/rulez.data","rulez_data.txt"],
["http://new.host/more_data.doc","more_data.doc"],
# ...
);
my $chunk_size = 2;
my $conf = qq(
log4perl.rootLogger = DEBUG, SYSLOG
log4perl.appender.SYSLOG = Log::Dispatch::Syslog
log4perl.appender.SYSLOG.min_level = debug
log4perl.appender.SYSLOG.ident = $0 [$$]
log4perl.appender.SYSLOG.facility = daemon
log4perl.appender.SYSLOG.layout = Log::Log4perl::Layout::SimpleLayout
);
Log::Log4perl::init( \$conf );
my $logger = Log::Log4perl->get_logger();
# Max 30 processes for parallel download
my $pm = Parallel::ForkManager->new(30);
while ( my @chunk = splice @links, 0, $chunk_size) {
$pm->start and next;
$logger->info((scalar @chunk)." links to download");
for my $linkarray (@chunk){
my ($link,$fn) = @$linkarray;
my $status = getstore($link,$fn);
$logger->warn("Cannot get $fn from $link") if $status != RC_OK;
}
$pm->finish;
}
$pm->wait_all_children;
$logger->info("done");
Скрипт работал, но странновато…
Судя по логу, все неудачные скачивания приходились на один и тот же процесс-воркер. Странное совпадение.
Впрочем, судя по логу, вообще вся обработка ссылок происходила в одном и том же процессе. А как же параллельность, форки, воркеры?
Проблема в том, что логгер инициализируется в родительском процессе, и в момент инициализации вычисляется строка-идентификатор с именем и номером текущего, то есть родительского процесса. Дочерние процессы получают готовый объект-логгер, и их записи в логе все равно помечены родительским pid-ом.
Решением было бы инициализировать логгер в каждом процессе отдельно.