2013-08-24 16 views
7

Jeśli używam wget odzyskać coś z serwera geonames.org, zgłasza dwa adresy IP, a pierwszy z nich zawiedzie, ale robi to z drugiego:Jak awaryjnie

Resolving ws.geonames.org (ws.geonames.org)... 5.9.41.208, 176.9.107.169 
Connecting to ws.geonames.org (ws.geonames.org)|5.9.41.208|:80... failed: Connection refused. 
Connecting to ws.geonames.org (ws.geonames.org)|176.9.107.169|:80... connected. 
HTTP request sent, awaiting response... 200 OK 

Ale niestety musi uzyskać do niego dostęp poprzez perl za pomocą LWP :: UserAgent i HTTP :: Request. Jak mogę sprawić, by spróbowały drugiego adresu IP, jeśli pierwsza się nie powiedzie?

my $ua = LWP::UserAgent->new; 
my $req = HTTP::Request->new(
    GET => 
     "http://ws.geonames.org/countrySubdivision?lat=$lat&lng=$long&radius=$radius&username=xyzzy"); 

my $res = $ua->request($req); 
+0

jesteś pewien, że nie jest to zachowanie domyślne dla LWP :: UserAgent Anyway? Po skopiowaniu kodu LWP :: UserAgent dzisiaj, wydaje się, że kiedy gniazdo strumieniowe jest podłączone głęboko w IO :: Socket :: INET :: configure(), spróbuje wszystkich adresów IP zwróconych dla danej nazwy hosta i będzie rozlicza się za pierwsze IP, z którym może się połączyć. –

+0

@RobWells, problem polegał na tym, że istnieje różnica między "można otworzyć gniazdo" i "uzyskać prawidłową odpowiedź HTTP w czasie timeout". 'wget' przejdzie do następnego, jeśli druga część się nie powiedzie, ale' LWP :: UserAgent' nie. –

+1

Pozdrawiam @Paul. Zauważyłem, że musisz wyraźnie włączyć opcję MultiHome, aby uzyskać takie zachowanie w pętli w wielu IP. Jest pochowany w bagnie Perla OO -> SUPER :: foo, więc wyśledzenie jest "zabawne"! (-: –

Odpowiedz

6

Można to zrobić samemu: uzyskać wszystkie adresy IP za pomocą Net::DNS::Resolver, a następnie spróbuj wszystkie adresy IP, aż do uzyskania skutecznej odpowiedzi. Pamiętaj, że musisz zaopatrzyć się w nagłówek "Host", jeśli pracujesz z adresem IP, na wypadek gdyby serwer wykonywał wirtualne hosty oparte na nazwach.

Może działać coś takiego jak poniższe linie. Możliwe jest nawet moduł CPAN do tego nie sprawdzałem:

use Net::DNS; 
use LWP::UserAgent; 

my @addrs; 
{ 
    my $res = Net::DNS::Resolver->new; 
    my $query = $res->search("ws.geonames.org"); 
    if ($query) { 
     for my $rr ($query->answer) { 
      if ($rr->type eq "A") { 
       push @addrs, $rr->address; 
      } 
     } 
    } else { 
     die "DNS query failed: ", $res->errorstring, "\n"; 
    } 
} 

my $ua = LWP::UserAgent->new; 

my $res; 
for my $addr (@addrs) { 
    $res = $ua->get("http://$addr/countrySubdivision?lat=$lat&lng=$long&radius=$radius&username=xyzzy", Host => 'ws.geonames.org'); 
    last if $res->is_success; 
} 
+0

Przepraszam, naprawdę nie widziałem tego przed napisaniem komentarza, dlatego usunąłem komentarz, ponieważ w ogóle go nie dotyczy –

+0

OK, usunąłem również moją odpowiedź :-) –

1

Rozwiązanie z Slaven jest OK, z wyjątkiem, gdy adresy IP nie są bezpośrednio dostępne. W takim przypadku, następujące prace dla mnie:

local @LWP::Protocol::http::EXTRA_SOCK_OPTS = (
              PeerAddr => 'my_hostname', 
              MultiHomed => 1, 
             ); 
my $response = $ua->post('https://my_hostname/...', ...);