Pergunta

Estou tentando implementar uma solicitação para um servidor não confiável.A solicitação é bom ter, mas não é 100% necessário para o meu script perl para concluir com êxito.O problema é que o servidor irá ocasionalmente impasse (estamos tentando descobrir o porquê) e a solicitação nunca terá sucesso.Uma vez que o servidor acha que ele está vivo, ele mantém a conexão de soquete aberto, portanto, LWP::UserAgent do valor de tempo de espera não nos faz nada bem que assim sempre.Qual é a melhor maneira de impor uma absoluta de tempo limite em um pedido?

FYI, isso não é um problema de DNS.O impasse tem algo a ver com um enorme número de atualizações batendo em nosso banco de dados Postgres ao mesmo tempo.Para fins de teste, temos, essencialmente, colocar um while(1) {} linha em servidores de resposta do manipulador.

Atualmente, o código fica assim:

my $ua = LWP::UserAgent->new;
ua->timeout(5); $ua->cookie_jar({});

my $req = HTTP::Request->new(POST => "http://$host:$port/auth/login");
$req->content_type('application/x-www-form-urlencoded');
$req->content("login[user]=$username&login[password]=$password");

# This line never returns 
$res = $ua->request($req);

Tentei usar sinais para acionar um limite de tempo, mas que não parecem funcionar.

eval {
    local $SIG{ALRM} = sub { die "alarm\n" };
    alarm(1);
    $res = $ua->request($req);
    alarm(0);
};
# This never runs
print "here\n";

A resposta final eu vou usar foi proposto por alguém offline, mas eu vou falar aqui.Por algum motivo, SigAction funciona enquanto $SIG(ALRM) não.Ainda não sei por que, mas este foi testado para funcionar.Aqui estão duas versões:

# Takes a LWP::UserAgent, and a HTTP::Request, returns a HTTP::Request
sub ua_request_with_timeout {
    my $ua = $_[0];
    my $req = $_[1];
    # Get whatever timeout is set for LWP and use that to 
    #  enforce a maximum timeout per request in case of server
    #  deadlock. (This has happened.)
    use Sys::SigAction qw( timeout_call );
    our $res = undef;
    if( timeout_call( 5, sub {$res = $ua->request($req);}) ) {
        return HTTP::Response->new( 408 ); #408 is the HTTP timeout
    } else {
        return $res;
    }
}
sub ua_request_with_timeout2 {
    print "ua_request_with_timeout\n";
    my $ua = $_[0];
    my $req = $_[1];
    # Get whatever timeout is set for LWP and use that to 
    #  enforce a maximum timeout per request in case of server
    #  deadlock. (This has happened.)
    my $timeout_for_client = $ua->timeout() - 2;
    our $socket_has_timedout = 0;

    use POSIX;
    sigaction SIGALRM, new POSIX::SigAction(
                                            sub {
                                                $socket_has_timedout = 1;
                                                die "alarm timeout";
                                            }
                                            ) or die "Error setting SIGALRM handler: $!\n";
    my $res = undef;
    eval {
        alarm ($timeout_for_client);
        $res = $ua->request($req);
        alarm(0);
    };
    if ( $socket_has_timedout ) {
        return HTTP::Response->new( 408 ); #408 is the HTTP timeout
    } else {
        return $res;
    }
}
Foi útil?

Solução

Você pode tentar LWPx::ParanoidAgent, uma subclasse de LWP::UserAgent que é mais cauteloso sobre a forma como ele interage com servidores web remotos.

Entre outras coisas, ele permite que você especificar um limite de tempo global.Ele foi desenvolvido por Brad Fitzpatrick como parte do LiveJournal projeto.

Outras dicas

Você pode fazer o seu próprio tempo de espera como este:

use LWP::UserAgent;
use IO::Pipe;

my $agent = new LWP::UserAgent;

my $finished = 0;
my $timeout = 5;

$SIG{CHLD} = sub { wait, $finished = 1 };

my $pipe = new IO::Pipe;
my $pid = fork;

if($pid == 0) {
    $pipe->writer;
    my $response = $agent->get("http://stackoverflow.com/");
    $pipe->print($response->content);
    exit;
}

$pipe->reader;

sleep($timeout);

if($finished) {
    print "Finished!\n";
    my $content = join('', $pipe->getlines);
}   
else {
    kill(9, $pid);
    print "Timed out.\n";
}   

Pelo que eu entendi, a propriedade de tempo de espera não levará em conta os limites DNS.É possível que você poderia fazer uma pesquisa de DNS separadamente, em seguida, fazer o pedido para o servidor se que funciona, com o correto valor de tempo limite definido para o agente.

É este um problema de DNS com o servidor, ou algo mais?

EDITAR:Ele também pode ser um problema com o IO::Socket.Tente atualizar o IO::Socket módulo, e veja se isso ajuda.Eu tenho certeza que não foi um bug que estava impedindo LWP::UserAgent tempos de espera de trabalho.

Alex

A seguinte generalização de uma das respostas também restaura o sinal de alarme processador anterior manipulador e adiciona uma segunda chamada de alarme(0) no caso de a chamada eval relógio lança um alarme não exceção, e quer cancelar o alarme.Adicional de us $@ inspeção e manipulação pode ser adicionado:

sub ua_request_with_timeout {
    my $ua = $_[0];
    my $request = $_[1];

    # Get whatever timeout is set for LWP and use that to 
    #  enforce a maximum timeout per request in case of server
    #  deadlock. (This has happened.)`enter code here`
    my $timeout_for_client_sec = $ua->timeout();
    our $res_has_timedout = 0; 

    use POSIX ':signal_h';

    my $newaction = POSIX::SigAction->new(
        sub { $res_has_timedout = 1; die "web request timeout"; },# the handler code ref
        POSIX::SigSet->new(SIGALRM),
        # not using (perl 5.8.2 and later) 'safe' switch or sa_flags
    );  

    my $oldaction = POSIX::SigAction->new();
    if(!sigaction(SIGALRM, $newaction, $oldaction)) {
        log('warn',"Error setting SIGALRM handler: $!");
        return $ua->request($request);
    }   

    my $response = undef;
    eval {
        alarm ($timeout_for_client_sec);
        $response = $ua->request($request);
        alarm(0);
    }; 

    alarm(0);# cancel alarm (if eval failed because of non alarm cause)
    if(!sigaction(SIGALRM, $oldaction )) {
        log('warn', "Error resetting SIGALRM handler: $!");
    }; 

    if ( $res_has_timedout ) {
        log('warn', "Timeout($timeout_for_client_sec sec) while waiting for a response from cred central");
        return HTTP::Response->new(408); #408 is the HTTP timeout
    } else {
        return $response;
     }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top