在处理一些遗留代码时,我被以下问题难住了。
我们有一个程序可以自动将配置推送到多个路由器。执行此操作的Perl程序使用Parallel::ForkManager
库来完成这些推送的并行化。
在程序的开头出现了以下语句…
$p = new Parallel::ForkManager($multi);
创建分叉的主循环如下…
foreach my $node (@files) {
# only if we're running in parallel
if (ref($p)) {
$p->start() and next;
}
# ship the file & path off to the main sub that does
# all the heavy lifting
do_push($node);
# only if we're running in parallel
if (ref($p)) {
$p->finish();
}
}
# only if we're running in parallel
if (ref($p)) {
$p->wait_all_children();
}
do_push($node)
子例程太长,无法在这里详细说明,但简而言之,它为节点建立一个日志文件,然后通过ssh连接到路由器并推送配置。然后退出并完成日志记录。
从表面上看,一切似乎都在工作,进程分叉,结果如预期的那样。
然而…当单独的进程被创建时,路由器的连接和配置的推送并不是并行发生的。很容易通过netstat -an
验证ssh连接只是按顺序发生,而不是并行发生。只有在一个连接关闭后,下一个路由器才会连接。简而言之,我一次只能获得一个ssh连接。
有没有人知道为什么会这样,或者在哪里继续解决这个问题?
[编辑]根据注释,这里是子程序中发生的事情的摘要。出于篇幅和安全的考虑,我不能全部放在这里。
子程序的开头如下…
sub do_push {
my($data) = @_;
my($path,$router) = @$data;
$|++; # hopefully speed things up
($DEBUG or ($v > 2)) && print STDERR "PROCESSING: $router ($path)n";
接下来的过程是…
- 打开一个日志文件,写入文件。
- 实例化连接路由器的模块
- 连接路由器
- 推送命令,验证命令,如果成功则提交。
- 记录所有的时间到日志文件。
子程序的结尾如下…
# finished with this router...
# append our archive file
my $ts = tv_interval($t0, [gettimeofday()]);
(!$DEBUG && $archive) && print S "ROUTER: $router:$ts:$dispositionn";
($DEBUG or ($v > 2)) && print STDERR "ROUTER COMPLETE: $router -> $ts secn";
# unlock and close our files
(($DEBUG > 1) or ($v > 2)) && print STDERR "UNLOCK AND CLOSE: $archfilen";
(($DEBUG > 1) or ($v > 2)) && print STDERR "UNLOCK AND CLOSE: $summaryn";
(!$DEBUG && $archive) && flock(F, LOCK_UN);
(!$DEBUG && $archive) && flock(S, LOCK_UN);
(!$DEBUG && $archive) && close(F);
(!$DEBUG && $archive) && close(S);
return;
}
如果在调试模式下运行,子程序中的第一条调试语句:"PROCESSING: $router ($path)n"
只在前一个路由器完全完成子程序后打印。肯定有多个进程分叉,我已经验证了。只是"某些东西"阻止了进程运行,直到前一个进程完成。我在努力寻找那个"东西"是什么。
[solved]
我讨厌回答我自己的问题,但是我确实找到了阻塞的原因…
原来是文件访问问题。在do_push()
循环中,程序记录到两个文件。一个文件是用来记录结果的文件本身,这个文件是子进程独有的,没有问题。然而,该程序也写入了一个"摘要"文件。所有子进程都记录到这个文件。
将会发生的是,第一个线程将打开文件,记录一些东西,连接到路由器并推出命令。一旦完成,它将关闭文件。当然,其他线程会阻塞,等待访问"摘要"文件。
对于小型路由器推送,这一切都发生得如此之快,以至于看起来这一切都是并行发生的。只有当我们不得不向每个路由器推出1000条线路时,才发现这并不是并行进行的。