目前最舒适可靠的跨平台Perl模块用于并行下载是什么?

3
我需要通过简单的POST请求从一个URL下载多个数据集,并获得XML格式的返回结果。我可以通过同时进行多个请求来加快速度,但这里有个难点:它需要在Windows和Linux上运行,因此线程和分叉都不可用(由于这仅涉及IO操作,我认为它们也不是必需的)。此外,我的同事对Perl的理解水平不是很高,但需要能够掌握如何使用它(不一定需要理解其内部原理,只需了解使用方法即可)。因此,如果API相对简单,我会很高兴。目前我正在考虑使用IO::Lambda。还有其他建议吗?根据draegtun的建议,我现在已经完成了这个任务,这个方案非常完美:https://gist.github.com/661386。你可能很快就会在CPAN上看到它。

3
为什么线程和分叉被淘汰了?在Windows系统上,可以使用fork(最好使用Parallel::ForkManager)进行编程,但需要注意一些细节:http://perldoc.perl.org/perlfork.html - Sinan Ünür
2
你打算如何实现并行解决方案,如果禁止使用fork或线程?通过select掩码处理I/O多路复用不是一件容易的事情。 - tchrist
Sinan:在Linux下,线程不可靠,特别是在旧版本的Perl中。在Windows下,分叉根本不起作用。 ||| tchrist:IO::Lambda提供了处理选择过程的工具,但似乎在POST请求方面存在问题。我基本上希望有人上传到CPAN一个可以正确执行此操作的模块。 - Mithaldu
1
线程可能有些棘手,但我绝对不会在不能使用基本的多进程技术如fork(2)的环境下工作。同样地,如果被迫使用古老的Perl也是如此。此外,我不知道在Cygwin下构建的Perl中使用fork是否存在任何问题。任何低于这个标准的都是无法容忍的。 - tchrist
让我这样说吧:我的同事们从来没有写过一行测试代码,甚至不知道如何写。我面临着一个艰巨的挑战。 :/ - Mithaldu
3个回答

6
请看 AnyEvent::HTTP。根据CPAN测试平台矩阵,它可以在Windows上编译并工作。
下面是一个简单的异步POST示例(http_post)。
use 5.012;
use warnings;
use AnyEvent::HTTP;

my $cv = AnyEvent->condvar;

my @urls = (
    [google => 'http://google.com', 'some body'],
    [yahoo  => 'http://yahoo.com' , 'any body' ],
);

for my $site (@urls) {
    my ($name, $url, $body) = @$site;
    $cv->begin; 
    http_post $url, $body => sub {
        my $xml = shift;
        do_something_with_this( $name, $xml );
        $cv->end;
    }
}

# wait till all finished
$cv->recv;
say "Finished";

sub do_something_with_this { say @_ }

注意,无论你决定用do_something_with_this做什么,都要避免任何阻塞。请查看其他非阻塞的AnyEvent模块

/I3az/


现在尝试应用这个。我该如何使用POST方法? - Mithaldu
1
http_post $url, $body => sub { ... };。已编辑以显示如何发布body(注意,我的body只是示例,因此它是垃圾数据,与这些站点无关)。 - draegtun
谢谢,这似乎完美地运作了。我发现最简单的方法是使用HTTP::Request::Common生成POST请求并使用http_post $req->uri->as_string, $req->content, sub{};来处理它。 - Mithaldu
你是否考虑过编写两个并行实现,一个针对每种架构?use Config; if ($Config{osname} eq 'linux') { ... } elsif { ... } - Ether
我想了一秒钟,但这只会增加更多的工作量,因为在Perl中,在Windows上实现一个并行下载器本身就不容易。 - Mithaldu
另外,我已经在努力制作一个合适的模块了。 :) https://gist.github.com/661341 - Mithaldu

5

您可以尝试使用LWP::Parallel

更新:

我刚试图在Windows XP上使用ActiveState的5.10.1版本构建它,并遇到了许多测试失败,其中一些是由于TEST脚本盲目地将..添加到@INC中的所有条目,其他一些似乎是由于与LWP::Protocol::*类的版本不匹配。

这是一个问题。我可能会使用Parallel::ForkManagerLWP一起使用。

#!/usr/bin/perl

use strict; use warnings;
use Config::Std { def_sep => '=' };
use File::Slurp;
use HTTP::Request::Common qw(POST);
use LWP::UserAgent;
use Parallel::ForkManager;

die "No config file specified\n" unless @ARGV;
my ($ini) = @ARGV;

read_config $ini, my %config;

my $pm = Parallel::ForkManager->new(10);

my @urls = @{ $config{''}{url} };

for my $url ( @urls ) {
    $pm->start and next;
    my $param = [ %{ $config{$url} } ];
    my $request = POST $url, $param;
    my $ua = LWP::UserAgent->new;
    my $fn = sprintf '%s-%s-%s.xml',
                     map $request->$_, qw( method uri content);
    $fn =~ s/\W+/_/g;
    my $response = $ua->request( $request );
    if ( $response->code == 200 ) {
        write_file $fn, \ $response->as_string;
    }
    else {
        warn $response->message, "\n";
    }
    $pm->finish;
}
$pm->wait_all_children;

以下是示例配置文件:
url = http://one.example.com/search
url = http://two.example.com/query
url = http://three.example.com/question
[http://one.example.com/search] keyword = Perl limit = 20
[http://two.example.com/query] type = Who is limit = 10
[http://three.example.com/question] use = Perl result = profit

更新:

如果你需要确信执行不是串行的,请尝试以下简短的脚本:

#!/usr/bin/perl

use strict; use warnings;

use Parallel::ForkManager;

my $pm = Parallel::ForkManager->new(2);

for my $sub (1 .. 4) {
    $pm->start and next;
    for my $i ('a' .. 'd') {
        sleep rand 3;
        print "[$sub]: $i\n";
    }
    $pm->finish;
}

$pm->wait_all_children;

输出:

[1]:a
[1]:b
[2]:a
[1]:c
[1]:d
[2]:b
[3]:a
[3]:b
[3]:c
[2]:c
[3]:d
[2]:d
[4]:a
[4]:b
[4]:c
[4]:d

关于您对“可靠性”的评论,我认为这是错误的。您正在模拟以下脚本:

#!/usr/bin/perl

use strict; use warnings;

use Parallel::ForkManager;
use YAML;

my @responses = parallel_run();

print Dump \@responses;

sub parallel_run {
    my $pm = Parallel::ForkManager->new(2);
    my @responses;
    for my $sub (1 .. 4) {
        $pm->start and next;
        for my $i ('a' .. 'd') {
            sleep rand 3;
            push @responses, "[$sub]: $i";
        }
        $pm->finish;
    }
    $pm->wait_all_children;
    return @responses;
}

你从中得到的输出将是:
--- []
你需要自己找出原因。这就是为什么Parallel::ForkManager允许您注册回调函数。就像您在使用AnyEvent::HTTP时使用的那些一样。
你使用什么模块是你自己的事情。只是不要一直发表明显错误的声明。

@Mithaldu 当然我在Windows上试过了。我大部分时间都在Windows上。请查看更新的答案。我基本上已经为您完成了工作,而且负评并不合适。 - Sinan Ünür
你需要哪个中间文件?ini文件用于将代码与数据分离。如果请求可以硬编码,请随意忽略它。此外,您不必将输出写入文件...可以根据文档中“数据结构检索”部分的说明注册回调函数。 - Sinan Ünür
1
你检查过运行后 @responses 包含什么了吗?我认为现在是时候意识到语句“我的同事对 Perl 的理解水平不是很高”需要修订了。正如我所说,如果你需要从子进程中获取数据,请注册回调函数。是的,P:FM 可能会使用更多内存,但你现在所做的也没有帮助到你。 - Sinan Ünür
不,我没有研究过那个,因为如果我尝试它,我的调试器就会做到这一点:https://gist.github.com/661144#file_gistfile2.txt(还记得我提到的可靠性吗?;))与此同时,这真是太完美了:https://gist.github.com/661177 - Mithaldu
我可能在这里犯过几次错误,但是一旦并行化方案在我的调试器中开始抛出内存错误,我就会离开它。简单明了。可靠性问题不是因为显而易见的方法无法奏效,而是因为它引发了内存错误。 - Mithaldu
显示剩余5条评论

1

Mojo::UserAgent 也可以进行异步并行的http请求。相对于其他模块,它的API可能更容易被非Perl人员理解。

目前还不确定它是否足够“可靠”。。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接