How do I make parallel HTTP requests in Perl, and

2020-02-10 08:57发布

问题:

Using Perl, I'm looking for a simple way to perform a handful of HTTP requests in parallel, where I get responses back in the same order I sent them after they complete, e.g.:

my ($google, $perl) = foobar(GET => 'http://www.google.com/',
                             GET => 'http://www.perl.org/');

Is there a module I should be looking at?

I know I can do the bookkeeping by hand, but I feel spoiled after being able to do this using jQuery's when method, and I'd love to have as simple a solution using Perl.

Thanks for your help.

回答1:

use threads;
use LWP::UserAgent qw( );

my $ua = LWP::UserAgent->new();
my @threads;
for my $url ('http://www.google.com/', 'http://www.perl.org/') {
   push @threads, async { $ua->get($url) };
}

for my $thread (@threads) {
   my $response = $thread->join;
   ...
}

The best part is that the parent doesn't wait for all requests to be completed. As soon as the right request is completed, the parent will unblock to process it.


If you used Parallel::ForkManager or something else where you can't wait for a specific child, you can use the following code to order the results:

for my $id (0..$#urls) {
   create_task($id, $urls[$id]);
}

my %responses;
for my $id (0..$#urls) {
   if (!exists($responses{$id})) {
      my ($id, $response) = wait_for_a_child_to_complete();
      $responses{$id} = $response;
      redo;
   }

   my $response = delete($responses{$id});
   ...
}


回答2:

I am a fan of Mojo! From the Mojo::UserAgent documentation:

use Mojo;
use Mojo::UserAgent;
# Parallel requests
my $ua = Mojo::UserAgent->new;
$ua->max_redirects(5);
my $delay = Mojo::IOLoop->delay;
for my $url ('http://www.google.com/', 'http://www.perl.org/') {
  $delay->begin;
  $ua->get($url => sub {
    my ($ua, $tx) = @_;
    $delay->end($tx->res->dom);
  });
}
my @responses = $delay->wait;
print join "\n", @responses

Enjoy!

EDIT

Btw. you do not have to process the responses at the end, you may do it in between:

# ...
$ua->get($url => sub {
    my ($ua, $tx) = @_;
    $delay->end(1);
    # process $tx->res here
});
# ...
$delay->wait;