用于 Ping 大量主机的 Perl 脚本
Perl Script to Ping lots of hosts
我有很多主机(大约 40000+)需要使用 perl 脚本测试 ping。要求在 3-4 分钟之前完成此验证,我需要 write/dump ping results/output 到一个平面文件。另一个集成将读取此输出(可能是尾部)并生成一些警报。
我将在一台有 16 个 CPU(1 个套接字,每个套接字 8 个核心,每个核心 2 个线程)的机器上 运行 这个 perl 脚本。
我有 perl 5,版本 14,颠覆 1 (v5.14.1)
我使用了 'nmap' 实用程序,我的时间如下所示:
Nmap 完成:在 356.54 秒~6 分钟内扫描了 50233 个 IP 地址(39313 个主机)
Nmap 完成:在 641.93 秒~11 分钟内扫描了 79195 个 IP 地址(28178 个主机)
我可以使用多线程 perl
进一步加速吗?我安装了 Net::Ping 模块,但我对安装更多自定义模块不感兴趣。
更新##1
根据“Sobrique”的回答和示例代码,我有如下所示的有效代码。非常感谢您提供示例,因为它帮助我了解了 perl 中的 Thread::Queue 模块。
#!/usr/bin/perl
# Run under Oracle Perl, for DBI.
BEGIN {
my $ORACLE_HOME = "/opt/oracle/app/oracle/product/12.1.0/client_1";
$ENV{ORACLE_HOME}=$ORACLE_HOME;
die "ORACLE_HOME not set\n" unless $ENV{ORACLE_HOME};
unless ($ENV{OrAcLePeRl}) {
$ENV{OrAcLePeRl} = "$ENV{ORACLE_HOME}/perl";
$ENV{PERL5LIB} =
"$ENV{PERL5LIB}:$ENV{OrAcLePeRl}/lib:$ENV{OrAcLePeRl}/lib/site_perl";
$ENV{LD_LIBRARY_PATH} =
"$ENV{LD_LIBRARY_PATH}:$ENV{ORACLE_HOME}/lib32:$ENV{ORACLE_HOME}/lib";
exec "$ENV{OrAcLePeRl}/bin/perl", [=10=], @ARGV;
}
}
use strict;
use warnings;
use threads;
use Thread::Queue;
my $process_q = Thread::Queue->new();
#insert tasks into thread queue.
open( my $input_fh, "<", "server_list" ) or die $!;
$process_q->enqueue(<$input_fh>);
close($input_fh);
my $OutputFile = "PingOutput.mout";
open (OUTPUT_FILEH, ">>$OutputFile") or die "Can't Open $OutputFile : $!";
# Worker threads
my $thread_limit = 25;
# send markers.
$process_q->enqueue(undef) for 1..$thread_limit;
my @thr = map {
threads->create(sub {
while (defined (my $server = $process_q->dequeue())) {
chomp($server);
print OUTPUT_FILEH threads->self()->tid() . ":pinging$server\n";
my $result = `/usr/bin/ping -c 1 $server`;
print OUTPUT_FILEH $result;
}
});
} 1..$thread_limit;
# terminate.
$_->join() for @thr;
您确实可以使用多线程来进一步加快速度。这是使用工作队列的完美场景:
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue;
my $nthreads = 5;
my $process_q = Thread::Queue->new();
my $failed_q = Thread::Queue->new();
#this is a subroutine, but that runs 'as a thread'.
#when it starts, it inherits the program state 'as is'. E.g.
#the variable declarations above all apply - but changes to
#values within the program are 'thread local' unless the
#variable is defined as 'shared'.
#Behind the scenes - Thread::Queue are 'shared' arrays.
sub worker {
#NB - this will sit a loop indefinitely, until you close the queue.
#using $process_q -> end
#we do this once we've queued all the things we want to process
#and the sub completes and exits neatly.
#however if you _don't_ end it, this will sit waiting forever.
while ( my $server = $process_q->dequeue() ) {
chomp($server);
print threads->self()->tid() . ": pinging $server\n";
my $result = `/bin/ping -c 1 $server`;
if ($?) { $failed_q->enqueue($server) }
print $result;
}
}
#insert tasks into thread queue.
open( my $input_fh, "<", "server_list" ) or die $!;
$process_q->enqueue(<$input_fh>);
close($input_fh);
#we 'end' process_q - when we do, no more items may be inserted,
#and 'dequeue' returns 'undefined' when the queue is emptied.
#this means our worker threads (in their 'while' loop) will then exit.
$process_q->end();
#start some threads
for ( 1 .. $nthreads ) {
threads->create( \&worker );
}
#Wait for threads to all finish processing.
foreach my $thr ( threads->list() ) {
$thr->join();
}
#collate results. ('synchronise' operation)
while ( my $server = $failed_q->dequeue_nb() ) {
print "$server failed to ping\n";
}
(注:我的代码,来自我给出的另一个答案:Perl daemonize with child daemons)
但是 - 我认为您不会发现比 nmap
更快的东西,因为它已经以非常适合网络扫描场景的方式进行并行处理。
我有很多主机(大约 40000+)需要使用 perl 脚本测试 ping。要求在 3-4 分钟之前完成此验证,我需要 write/dump ping results/output 到一个平面文件。另一个集成将读取此输出(可能是尾部)并生成一些警报。
我将在一台有 16 个 CPU(1 个套接字,每个套接字 8 个核心,每个核心 2 个线程)的机器上 运行 这个 perl 脚本。 我有 perl 5,版本 14,颠覆 1 (v5.14.1) 我使用了 'nmap' 实用程序,我的时间如下所示:
Nmap 完成:在 356.54 秒~6 分钟内扫描了 50233 个 IP 地址(39313 个主机)
Nmap 完成:在 641.93 秒~11 分钟内扫描了 79195 个 IP 地址(28178 个主机)
我可以使用多线程 perl
进一步加速吗?我安装了 Net::Ping 模块,但我对安装更多自定义模块不感兴趣。
更新##1
根据“Sobrique”的回答和示例代码,我有如下所示的有效代码。非常感谢您提供示例,因为它帮助我了解了 perl 中的 Thread::Queue 模块。
#!/usr/bin/perl
# Run under Oracle Perl, for DBI.
BEGIN {
my $ORACLE_HOME = "/opt/oracle/app/oracle/product/12.1.0/client_1";
$ENV{ORACLE_HOME}=$ORACLE_HOME;
die "ORACLE_HOME not set\n" unless $ENV{ORACLE_HOME};
unless ($ENV{OrAcLePeRl}) {
$ENV{OrAcLePeRl} = "$ENV{ORACLE_HOME}/perl";
$ENV{PERL5LIB} =
"$ENV{PERL5LIB}:$ENV{OrAcLePeRl}/lib:$ENV{OrAcLePeRl}/lib/site_perl";
$ENV{LD_LIBRARY_PATH} =
"$ENV{LD_LIBRARY_PATH}:$ENV{ORACLE_HOME}/lib32:$ENV{ORACLE_HOME}/lib";
exec "$ENV{OrAcLePeRl}/bin/perl", [=10=], @ARGV;
}
}
use strict;
use warnings;
use threads;
use Thread::Queue;
my $process_q = Thread::Queue->new();
#insert tasks into thread queue.
open( my $input_fh, "<", "server_list" ) or die $!;
$process_q->enqueue(<$input_fh>);
close($input_fh);
my $OutputFile = "PingOutput.mout";
open (OUTPUT_FILEH, ">>$OutputFile") or die "Can't Open $OutputFile : $!";
# Worker threads
my $thread_limit = 25;
# send markers.
$process_q->enqueue(undef) for 1..$thread_limit;
my @thr = map {
threads->create(sub {
while (defined (my $server = $process_q->dequeue())) {
chomp($server);
print OUTPUT_FILEH threads->self()->tid() . ":pinging$server\n";
my $result = `/usr/bin/ping -c 1 $server`;
print OUTPUT_FILEH $result;
}
});
} 1..$thread_limit;
# terminate.
$_->join() for @thr;
您确实可以使用多线程来进一步加快速度。这是使用工作队列的完美场景:
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue;
my $nthreads = 5;
my $process_q = Thread::Queue->new();
my $failed_q = Thread::Queue->new();
#this is a subroutine, but that runs 'as a thread'.
#when it starts, it inherits the program state 'as is'. E.g.
#the variable declarations above all apply - but changes to
#values within the program are 'thread local' unless the
#variable is defined as 'shared'.
#Behind the scenes - Thread::Queue are 'shared' arrays.
sub worker {
#NB - this will sit a loop indefinitely, until you close the queue.
#using $process_q -> end
#we do this once we've queued all the things we want to process
#and the sub completes and exits neatly.
#however if you _don't_ end it, this will sit waiting forever.
while ( my $server = $process_q->dequeue() ) {
chomp($server);
print threads->self()->tid() . ": pinging $server\n";
my $result = `/bin/ping -c 1 $server`;
if ($?) { $failed_q->enqueue($server) }
print $result;
}
}
#insert tasks into thread queue.
open( my $input_fh, "<", "server_list" ) or die $!;
$process_q->enqueue(<$input_fh>);
close($input_fh);
#we 'end' process_q - when we do, no more items may be inserted,
#and 'dequeue' returns 'undefined' when the queue is emptied.
#this means our worker threads (in their 'while' loop) will then exit.
$process_q->end();
#start some threads
for ( 1 .. $nthreads ) {
threads->create( \&worker );
}
#Wait for threads to all finish processing.
foreach my $thr ( threads->list() ) {
$thr->join();
}
#collate results. ('synchronise' operation)
while ( my $server = $failed_q->dequeue_nb() ) {
print "$server failed to ping\n";
}
(注:我的代码,来自我给出的另一个答案:Perl daemonize with child daemons)
但是 - 我认为您不会发现比 nmap
更快的东西,因为它已经以非常适合网络扫描场景的方式进行并行处理。