我使用WWW::Mechanize
编写了一个Perl脚本,它从文本文件中读取URL并逐个连接到它们。在每个操作中,它解析网页的内容,寻找一些特定的关键字,如果找到,它将被写入输出文件。
为了加快这一过程,我使用Parallel::ForkManager
并将MAX_CHILDREN
设置为3
。虽然我观察到速度有所提高,但问题是,一段时间后脚本崩溃了。 Perl.exe
进程被终止,并且不会显示任何特定的错误消息。
我已经多次运行脚本以查看它是否总是在同一点失败,但是故障点似乎是间歇性的。
请注意,我已经处理了WWW::Mechanize
和HTML::TreeBuilder::XPath
中的任何内存泄漏,如下所示:
WWW::Mechanize
,我设置了stack_depth(0)
,以便它不会缓存访问过的网页的历史记录。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 manager的finish方法之后添加了一个print语句,但是它没有打印出来。
我还使用了wait_all_children
方法,因为根据CPAN上的模块文档,它将等待处理为父进程的所有子进程重新开始。
我还不明白为什么,wait_all_children
方法位于while
或for
循环之外(如文档中所示),因为所有处理都在进行中在循环中。
感谢。
答案 0 :(得分:2)
为什么这个代码是用一个带有start
和finish
调用的主作业循环编写的,然后是循环外的wait_all_children
。它的工作原理如下:
<INPUT>
获取下一个作业。start
,导致子进程fork。此时,您有两个进程,每个进程在完全相同的点运行完全相同的代码。
3A。父进程点击or next
并跳回到顶部以阅读下一个<INPUT>
并开始进程。
3B。子进程没有点击or next
并继续运行您提供的代码,直到它出现在finish
,孩子退出。正如您所看到的,调用finish
之后循环中的任何代码都不会在父级中运行(因为它在循环中or next
之后不执行任何操作)或者孩子(因为他们在finish
退出)。
我从来没有使用过Parallel::ForkManager,但是如果你想在最后放一个print语句,看起来你可以放一个run_on_finished
钩子来完成一些代码。
要找到问题,我建议将start
和finish
之间的所有代码包装在eval
中,或者使用Try::Tiny和warn
输出错误,看是否有异常发生在那里打破它。当孩子去世时,我希望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()
执行了哪种错误处理,但您可能想重新访问它。