2016-04-27 6 views
5

Bazı URL'leri paralel olarak alan ve bazı işler yapan bir komut dosyası yazmam gerekiyor. Geçmişte bu tür şeyler için her zaman Parallel::ForkManager'u kullandım, ancak şimdi yeni bir şey öğrenmek istedim ve AnyEvent (ve AnyEvent::HTTP veya AnyEvent::Curl::Multi) ile eşzamansız programlama yapmayı denedim ... Ama AnyEvent'i anlamakta ve aşağıdakileri yapması gereken bir komut dosyası yazmakta sorun yaşıyorum: Belirli bir örnekte perl'de async'yi anlama

  • (her satır ayrı URL) bir dosyayı açmak
  • (ı yüklemek istemiyorum satırdan sonra
  • okuma dosyası satırı (şu andan itibaren paralel olarak, ancak fe 10 için limitli eşzamanlı istek ile) belleğe tüm dosya - büyük olabilir)
  • bir HTTP isteği oluşturmak f ya o URL
  • okuma tepkisi
  • güncellemeler MySQL rekor buna göre
  • (sonraki dosya hat)

Ben birçok kılavuzları, öğreticiler okumak, ama onun hala sert beni engelleme ve non arasındaki farkları anlamak için - kodlama kodu. Bay Szabo temellerini açıklar http://perlmaven.com/fetching-several-web-pages-in-parallel-using-anyevent, benzer senaryo bulduk, ama hala cant gibi bir şey nasıl uygulanacağını anlıyorum:

... 
open my $fh, "<", $file; 
while (my $line = <$fh>) 
{ 
# http request, read response, update MySQL 
} 
close $fh 
... 

... ve bu durumda bir eşzamanlılık sınırı ekleyin.

yardım için çok minnettar olacaktır;) Ben Net::Curl::Multi denemeye verdi Ikegami tavsiyelerine uyarak

GÜNCELLEME

. Sonuçlardan çok memnunum. Parallel::ForkManager'un sadece binlerce URL'yi eşzamanlı olarak yakaladığı yıllardan sonra, Net::Curl::Multi harika görünüyor. İşte dosyamda while döngüsündeki kodum var. Olması gerektiği gibi çalışıyor gibi görünüyor, ama ilk defa böyle bir şey yazma benim düşünceme göre daha deneyimli Perl kullanıcılarına bir göz atmak ve bazı potansiyel hatalar, kaçırdığım bir şey var mı diye bana sormak istiyorum. Ayrıca, eğer sorabilirmiyim: Net::Curl::Multi'un eşzamanlı çalışmasının nasıl çalıştığını tam olarak anlayamadığımdan, lütfen MySQL UPDATE komutunu (DBI aracılığıyla) RESPONSE döngüsünün içine yerleştirme ile ilgili herhangi bir sorun beklemem gerektiğini söyleyeyim (açık bir şekilde daha yüksek sunucu yükünün yanı sıra) 50 eşzamanlı N::C::M işçi ile çalıştırmak için komut dosyası, belki daha fazla).

#!/usr/bin/perl 

use Net::Curl::Easy qw(:constants); 
use Net::Curl::Multi qw(); 

sub make_request { 
    my ($url) = @_; 
    my $easy = Net::Curl::Easy->new(); 
    $easy->{url} = $url; 
    $easy->setopt(CURLOPT_URL,  $url); 
    $easy->setopt(CURLOPT_HEADERDATA, \$easy->{head}); 
    $easy->setopt(CURLOPT_FILE,  \$easy->{body}); 
    return $easy; 
} 

my $maxWorkers = 10; 

my $multi = Net::Curl::Multi->new(); 
my $workers = 0; 

my $i = 1; 
open my $fh, "<", "urls.txt"; 
LINE: while (my $url = <$fh>) 
{ 
    chomp($url); 
    $url .= "?$i"; 
    print "($i) $url\n"; 
    my $easy = make_request($url); 
    $multi->add_handle($easy); 
    $workers++; 

    my $running = 0; 
    do { 
     my ($r, $w, $e) = $multi->fdset(); 
     my $timeout = $multi->timeout(); 
     select $r, $w, $e, $timeout/1000 
     if $timeout > 0; 

     $running = $multi->perform(); 
     RESPONSE: while (my ($msg, $easy, $result) = $multi->info_read()) { 
      $multi->remove_handle($easy); 
      $workers--; 
      printf("%s getting %s\n", $easy->getinfo(CURLINFO_RESPONSE_CODE), $easy->{url}); 
     } 

     # dont max CPU while waiting 
     select(undef, undef, undef, 0.01); 
    } while ($workers == $maxWorkers || (eof && $running)); 
    $i++; 
} 
close $fh; 

cevap

5

Net :: Curl, oldukça hızlı olan oldukça iyi bir kitaplıktır. Ayrıca, paralel istekleri de halledebilir! Bunu AnyEvent yerine kullanmanızı tavsiye ederim.

use Net::Curl::Easy qw(:constants); 
use Net::Curl::Multi qw(); 

sub make_request { 
    my ($url) = @_; 
    my $easy = Net::Curl::Easy->new(); 
    $easy->{url} = $url; 
    $easy->setopt(CURLOPT_URL,  $url); 
    $easy->setopt(CURLOPT_HEADERDATA, \$easy->{head}); 
    $easy->setopt(CURLOPT_FILE,  \$easy->{body}); 
    return $easy; 
} 

my $max_running = 10; 
my @urls = ('http://www.google.com/'); 

my $multi = Net::Curl::Multi->new(); 
my $running = 0; 
while (1) { 
    while (@urls && $running < $max_running) { 
     my $easy = make_request(shift(@urls)); 
     $multi->add_handle($easy); 
     ++$running; 
    } 

    last if !$running; 

    my ($r, $w, $e) = $multi->fdset(); 
    my $timeout = $multi->timeout(); 
    select($r, $w, $e, $timeout/1000) 
     if $timeout > 0; 

    $running = $multi->perform(); 
    while (my ($msg, $easy, $result) = $multi->info_read()) { 
     $multi->remove_handle($easy); 
     printf("%s getting %s\n", $easy->getinfo(CURLINFO_RESPONSE_CODE), $easy->{url}); 
    } 
} 
+0

Ben "bir sürü alıyorum geri çağırma işlevi ayarlanmamış ". URL Ana Bilgisayarında bir alanın ne zaman olduğunu gösterir. IP kullanırsam bu hatayı alamıyorum. Ayrıca, eğer koyduysam 'print 'got it!';' # işlem $ easy' ise, sayfa içeriği otomatik olarak yazdırılır. – alan

+0

Bu nedenle, içeriğin yazdırılmasından çok, kolay bir şekilde saklanması. Geri arama hatası almıyorum?[Değişim ile deneyin. İlgili olabilir] – ikegami

+0

Yardımlarınız için teşekkürler. Maalesef hala "geri arama işlevi ayarlanmadı" alıyorum. Aslında 4 kez, o zaman printf'in. Nereden geldiğini bilmiyorum. – alan

2

Bu zaman uyumsuz bir şekilde, istiyorum ve güvenli bir şekilde Net::Curl sararak bunu yapmaz tam olarak ne yapar:

#!/usr/bin/env perl 

package MyDownloader; 
use strict; 
use warnings qw(all); 

use Moo; 

extends 'YADA::Worker'; 

has '+use_stats'=> (default => sub { 1 }); 
has '+retry' => (default => sub { 10 }); 

after init => sub { 
    my ($self) = @_; 

    $self->setopt(
     encoding   => '', 
     verbose    => 1, 
    ); 
}; 

after finish => sub { 
    my ($self, $result) = @_; 

    if ($self->has_error) { 
     print "ERROR: $result\n"; 
    } else { 
     # do the interesting stuff here 
     printf "Finished downloading %s: %d bytes\n", $self->final_url, length ${$self->data}; 
    } 
}; 

around has_error => sub { 
    my $orig = shift; 
    my $self = shift; 

    return 1 if $self->$orig(@_); 
    return 1 if $self->getinfo('response_code') =~ m{^5[0-9]{2}$}x; 
}; 

1; 

package main; 
use strict; 
use warnings qw(all); 

use Carp; 

use YADA; 

my $q = YADA->new(
    max  => 8, 
    timeout => 30, 
); 

open(my $fh, '<', 'file_with_urls_per_line.txt') 
    or croak "can't open queue: $!"; 
while (my $url = <$fh>) { 
    chomp $url; 

    $q->append(sub { 
     MyDownloader->new($url) 
    }); 
} 
close $fh; 
$q->wait; 
+1

Fikriniz harika ve tüm gereksinimlerimi karşılamamıza rağmen, Ikegami'nin çözümü benim için çok daha anlaşılır ve okunabilir. Girişiniz için teşekkürler. Aynı hedefe ulaşmak için birçok yol görmek harika. – alan

İlgili konular