簡體   English   中英

LWP :: UserAgent請求方法的真正超時

[英]True timeout on LWP::UserAgent request method

我正在嘗試向不可靠的服務器實現請求。 請求是一個很好的,但我的perl腳本不能100%成功完成。 問題是服務器偶爾會死鎖(我們試圖找出原因)並且請求永遠不會成功。 由於服務器認為它是活動的,它會使套接字連接保持打開狀態,因此LWP :: UserAgent的超時值對我們來說沒有任何好處。 對請求強制執行絕對超時的最佳方法是什么?

僅供參考,這不是DNS問題。 死鎖與大量同時訪問Postgres數據庫的更新有關。 出於測試目的,我們基本上在服務器響應處理程序中放置了while(1){}行。

目前,代碼如下所示:

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);

我嘗試使用信號來觸發超時,但這似乎不起作用。

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

我將要使用的最終答案是有人離線提出的,但我會在這里提到它。 出於某種原因,SigAction有效,而$ SIG(ALRM)卻沒有。 仍然不確定為什么,但這已經過測試可行。 這是兩個工作版本:

# 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;
    }
}

您可以嘗試LWPx :: ParanoidAgent ,LWP :: UserAgent的子類,它對如何與遠程Web服務器交互更加謹慎。

除此之外,它還允許您指定全局超時。 它由Brad Fitzpatrick開發,作為LiveJournal項目的一部分。

您可以像這樣制作自己的超時:

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";
}   

其中一個原始答案的以下概括還將警報信號處理程序恢復到前一個處理程序,並在eval時鍾中的調用引發非警報異常並且我們想要取消警報的情況下,向警報(0)添加第二個調用。 可以添加$ @檢查和處理:

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;
     }
}

根據我的理解,超時屬性不考慮DNS超時。 您可以單獨進行DNS查找,然后在服務器上發出請求,並為useragent設置正確的超時值。

這是服務器的DNS問題,還是其他什么?

編輯:它也可能是IO :: Socket的問題。 嘗試更新您的IO :: Socket模塊,看看是否有幫助。 我很確定那里有一個錯誤阻止LWP :: UserAgent超時工作。

亞歷克斯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM