Perl与Parallel :: ForkManager和WWW :: Mechanize崩溃

时间:2012-08-04 02:13:00

标签: perl parallel-processing fork www-mechanize

我使用WWW::Mechanize编写了一个Perl脚本,它从文本文件中读取URL并逐个连接到它们。在每个操作中,它解析网页的内容,寻找一些特定的关键字,如果找到,它将被写入输出文件。

为了加快这一过程,我使用Parallel::ForkManager并将MAX_CHILDREN设置为3。虽然我观察到速度有所提高,但问题是,一段时间后脚本崩溃了。 Perl.exe进程被终止,并且不会显示任何特定的错误消息。

我已经多次运行脚本以查看它是否总是在同一点失败,但是故障点似乎是间歇性的。

请注意,我已经处理了WWW::MechanizeHTML::TreeBuilder::XPath中的任何内存泄漏,如下所示:

  1. 对于WWW::Mechanize,我设置了stack_depth(0),以便它不会缓存访问过的网页的历史记录。
  2. HTML::TreeBuilder::XPath,我完成后删除根节点。这种方法帮助我解决了另一个不使用fork的类似脚本中的内存泄漏问题。
  3. 以下是脚本的结构,我在此仅提及相关部分,如果需要更多详细信息进行排查,请告知我们:

    #! /usr/bin/perl
    
    use HTML::TreeBuilder::XPath;
    use WWW::Mechanize;
    use warnings;
    use diagnostics;
    use constant MAX_CHILDREN => 3;
    
    open(INPUT,"<",$input) || die("Couldn't read from the file, $input with error: $!\n");
    open(OUTPUT, ">>", $output) || die("Couldn't open the file, $output with error: $!\n");
    
    $pm = Parallel::ForkManager->new(MAX_CHILDREN);
    
    $mech=WWW::Mechanize->new();
    $mech->stack_depth(0);
    
    while(<INPUT>)
    {
    chomp $_;
    $url=$_;
    
    $pm->start() and next;
    
    $mech->get($url);
    
    if($mech->success)
    {
        $tree=HTML::TreeBuilder::XPath->new();
        $tree->parse($mech->content);
    
        # do some processing here on the content and print the results to OUTPUT file
    
        # once done then delete the root node
    
        $tree->delete();
    }
    
    $pm->finish();
    
    print "Child Processing finished\n"; # it never reaches this point!
    
    }
    
    $pm->wait_all_children; 
    

    我想知道,为什么这个Perl脚本会在一段时间后仍然失败? 为了理解目的,我在fork manager的finish方法之后添加了一个print语句,但是它没有打印出来。 我还使用了wait_all_children方法,因为根据CPAN上的模块文档,它将等待处理为父进程的所有子进程重新开始。

    我还不明白为什么,wait_all_children方法位于whilefor循环之外(如文档中所示),因为所有处理都在进行中在循环中。

    感谢。

2 个答案:

答案 0 :(得分:2)

为什么这个代码是用一个带有startfinish调用的主作业循环编写的,然后是循环外的wait_all_children。它的工作原理如下:

  1. 父进程在每个循环开始时从<INPUT>获取下一个作业。
  2. 父运行start,导致子进程fork。此时,您有两个进程,每个进程在完全相同的点运行完全相同的代码。 3A。父进程点击or next并跳回到顶部以阅读下一个<INPUT>并开始进程。 3B。子进程没有点击or next并继续运行您提供的代码,直到它出现在finish,孩子退出。
  3. 同时,父进程正在忙着循环并每次创建一个子进程。在分配了3个孩子(或者你设置限制的任何东西)后,它会阻止,直到其中一个孩子退出。此时,它会立即产生一个新的孩子(每次为每个孩子产生一个步骤3b)。
  4. 当父项用尽时,它会跳出while循环(从不在其中运行任何内容),然后等待所有剩余的子项退出。
  5. 正如您所看到的,调用finish之后循环中的任何代码都不会在父级中运行(因为它在循环中or next之后不执行任何操作)或者孩子(因为他们在finish退出)。

    我从来没有使用过Parallel::ForkManager,但是如果你想在最后放一个print语句,看起来你可以放一个run_on_finished钩子来完成一些代码。

    要找到问题,我建议将startfinish之间的所有代码包装在eval中,或者使用Try::Tinywarn输出错误,看是否有异常发生在那里打破它。当孩子去世时,我希望STDERR会出现这样的事情,所以我不确定这会有所帮助。

    然而,它值得一试。这是我在代码中的建议,只显示了我将从中捕获异常的部分:

    # At the top add
    use Try::Tiny;
    
    # Later in your main loop
    
    $pm->start() and next;
    
    try {
    
        $mech->get($url);
    
        if($mech->success)
        {
            $tree=HTML::TreeBuilder::XPath->new();
            $tree->parse($mech->content);
    
            # do some processing here on the content and print the results to OUTPUT file
    
            # once done then delete the root node
    
            $tree->delete();
        }
    }
    
    catch {
        warn "Bad Stuff: ", $_;
    };
    
    $pm->finish();
    

    这可能会帮助您了解出了什么问题。

    如果它没有帮助,你可以尝试移动try块以包含更多的程序(就像use Try::Tiny行之后的几乎所有程序一样),看看是否有任何说明。

答案 1 :(得分:0)

$pm->wait_all_children;函数调用等待子进程结束的“ALL”并发出阻塞锁。我不确定您在$mech语句中对if()执行了哪种错误处理,但您可能想重新访问它。

相关问题