2012-08-04 69 views
1

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

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

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

请注意,我已经照顾任何内存泄漏的WWW::MechanizeHTML::TreeBuilder::XPath如下:

  1. 对于WWW::Mechanize,我设置stack_depth(0),这样它不缓存访问过的网页的历史记录。
  2. HTML::TreeBuilder::XPath,我完成后就删除根节点。这种方法帮助我解决了另一个不使用fork的类似脚本中的内存泄漏问题。

这里是脚本,我这里提到的只是相关部分的结构,请让我知道,如果需要更多的信息来解决:

#! /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管理器的完成方法之后添加了一个print语句,但它不打印它。 我也使用过,wait_all_children方法,因为根据CPAN上的模块的文档,它将等待处理过程来完成父进程的所有子进程。

我没有理解原因,wait_all_children方法是while以外的地方或for环路虽然(如文档中观察到的为好),因为所有的处理正在循环内发生。

谢谢。

回答

2

至于为什么这个代码是用一个主作业循环写入的,其中startfinish调用,然后在循环之外跟着一个wait_all_children。它的工作原理是这样的:

  1. 父进程在每个循环开始时从<INPUT>获取下一个作业。
  2. 父级运行start,这会导致子进程分叉。此时,您有两个进程,每个进程在完全相同的点上运行完全相同的代码。 3)。父进程命中or next并跳回顶部读取下一个<INPUT>并开始该过程。 3b。子进程没有命中or next,并继续运行您提供的代码,直到它遇到finish,即孩子退出的地方。
  3. 与此同时,父进程正忙于通过循环并每次都创建一个子进程。在分岔3个孩子(或者任何你设置的限制)之后,它会阻塞,直到其中一个孩子离开。此时,它立即产生一个新的孩子(每次为每个孩子产生步骤3b)。
  4. 当父母完成作业时,它跳出while循环(从不在其中运行任何东西),然后等待所有剩余的孩子退出。

正如你所看到的,在finish后环任何代码被称为永远不会在父运行(因为它没有在循环中or next后做什么)或孩子(因为他们退出在finish)。

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

发现问题,不过,我建议在eval包装startfinish之间的所有代码或使用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之后)并查看是否有任何说明。

+0

嗨,谢谢你的帮助。我试着放置try和catch块来查看错误消息的更多细节,但它不起作用。我甚至尝试在其中包含更多的代码,但这会使脚本更早失败,而不会提供任何错误消息细节。 – 2012-08-04 03:11:43

+0

try块只是试图揭示问题。如果不能看到'#在这里做些什么处理“,那么除了这些之外,很难提供更多的帮助。这里的分叉使得一些调试变得单调乏味。你可以添加一些警告语句,比如'warn'[$$]在机器得到\ n“'和'warn”之后[$$]在树删除\ n“'后了解它失败的位置。 '$$'可以让您按照子进程ID对警告日志条目进行分组。 – zostay 2012-08-04 11:31:49

+0

如果我通过私人信息向你发送剧本,你可以吗? – 2012-08-05 13:08:57

0

$pm->wait_all_children;函数调用等待“ALL”子进程结束并放置一个Blocking锁。我不确定您在if()声明中对$mech所做的错误处理类型,但您可能需要重新访问该声明。

+0

我想在任何时间点只有一个父母程序,它会在最多3个孩子的时候产卵。当任何一个孩子完成这项工作时,会产生一个新的儿童过程。我尝试过使用和不使用wait_all_children方法。对于$ mech的错误处理,我已经将$ mech-> get($ url)放在eval中,然后检查$ @以查看它是否为空。如果不是,则显示错误消息并继续。 – 2012-08-04 03:37:16

相关问题