【问题标题】:Add parallelism to perl script为 perl 脚本添加并行性
【发布时间】:2016-05-22 13:38:51
【问题描述】:

我有一个小的 perl 脚本,它从 mongoDB 获取服务详细信息,查询其状态并提供 html 输出

#...some stuff to get $token

my @cmd = ('/opt/mongo/bin/mongo', '127.0.0.1:27117/service_discovery', '--quiet', '-u', 'xxx', '-p', 'xxx', '--eval', "var environ='$env'; var action='status'", '/home/mongod/www/cgi/getstatus.js');
my $mongo_out;
run \@cmd, '>>', \$mongo_out;
$json->incr_parse ($mongo_out);
while (my $obj = $json->incr_parse) {
    my $hostname = "$obj->{'hostname'}";
    print "<tr><td colspan=4 align=\"center\"><h4>$hostname</h4></td></tr>";
    foreach my $service (@{$obj->{'services'}}) {
            my $name = "$service->{'name'}";
            my $port = "$service->{'port'}";
            my $proto = "$service->{'proto'}";
            my $request = HTTP::Request->new(GET => "${proto}://$hostname:${port}/status/service");
            $request->header(Authorization => "Bearer $token");
            my $ua = LWP::UserAgent->new;
            $ua->timeout(2);
            my $response = $ua->request($request);
            my $code = $response->code();
            if ($code == 200) {
                    my $var = %$response->{'_content'};
                    my $coder = JSON::XS->new->ascii->pretty->allow_nonref;
                    my $out = try {my $output = $coder->decode($var)} catch {undef};
                    if(exists $out->{'name'} && exists $out->{'version'}) {
                            print "<tr><td align=\"center\">$port</td><td align=\"center\">$name</td><td align=\"center\">$out->{'name'}</td><td align=\"center\">$out->{'version'}</td></tr>";
                    } else {
                            print "<tr><td align=\"center\">$port</td><td align=\"center\">$name</td><td colspan=2 align=\"center\">auth failed</td></tr>";
                    }
            } elsif ($code == 500) {
                            print "<tr><td align=\"center\">$port</td><td align=\"center\">$name</td><td colspan=2 align=\"center\">offline</td></tr>";
            } elsif ($code == 404) {
                    print "<tr><td align=\"center\">$port</td><td align=\"center\">$name</td><td colspan=2 align=\"center\">page not found</td></tr>";
            }
    }
}

它会执行一段时间,尤其是在某些服务离线时。是否可以同时查询同一主机内的服务?

【问题讨论】:

  • 一个 LWP::UserAgent 实例不能进行并发请求,但您可以在不同的线程或进程中同时使用多个 LWP::UserAgent 实例。 (即使在 Coro 线程中借助 LWP::Protocol::AnyEvent::http。)如果你想在一个线程中做所有事情,那么你可以使用 Net::CURL::Multi。 (奖励:它的开销远低于 LWP)

标签: perl parallel-processing


【解决方案1】:

这几乎是一个过于宽泛而无法回答的问题,因为......这取决于。

但是是的。在 perl 中你有两个半的并行机制:

  • thread
  • fork
  • 非阻塞 IO。

我说的是两个半,因为非阻塞 IO 并不是真正的并行,而是以不同的方式解决相同的问题。

实现并行性是一种非常好的方法,可以最终解决一些可怕且难以追踪的错误,并且需要稍微转变思维方式,因为您的代码不再以明确定义的顺序执行 - 重点是您的代码可能在不同的时间遇到​​不同的位,而 可能 导致完全混乱。

尤其是因为您导入的模块可能不是“线程安全的”(这意味着它们可能很好,但偶尔会以一种非常不可预测的方式中断,并且您会在试图追踪漏洞)。

考虑到这一点

线程

如果您使用过另一种语言的线程,可能会有点反直觉 - perl 线程不是轻量级的。启动它们的成本很高,尤其是因为您最终会有效地将内存占用量乘以正在运行的线程数。

因此,我通常会建议 - 查看“工作线程”模型,使用 Thread::Queue。您启动多个线程,并使用队列来序列化线程的输入和输出。

分叉

fork() 是一个 unix 本机系统调用。您经常使用它,而且效率很高。它将您的程序分成两个相同的副本——包括代码中的位置——在它被调用的地方。 唯一最初的区别是fork()系统调用的返回码——父进程将获得子进程ID,子进程将得到零。

意外地做奇怪的事情很容易,因为此时的两段代码在循环迭代、文件句柄等方面都处于完全相同的点,但这很快就会分歧,你可以再次得到一些如果您与“共享”资源交互,就会发生非常奇怪的事情。

我通常建议将Parallel::ForkManager 模块视为一种避免fork() 绊倒自己的简单方法。

非阻塞 IO

您通常可以使用类似IO::Selectcan_read 的方法,该方法检测如果您从中读取哪些文件句柄将被阻塞 - 您可以跳过那个,直到它阻塞。这也适用于您的用例,尽管它并不总是适用。

我在这里有以上两个例子:Perl daemonize with child daemons

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    • 2021-02-15
    • 1970-01-01
    • 2015-03-10
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    相关资源
    最近更新 更多