2008-09-16 18 views
10

मैं एक अविश्वसनीय सर्वर के लिए अनुरोध को लागू करने का प्रयास कर रहा हूं। अनुरोध करना अच्छा है, लेकिन सफलतापूर्वक पूरा करने के लिए मेरी perl स्क्रिप्ट के लिए 100% आवश्यक नहीं है। समस्या यह है कि सर्वर कभी-कभी डेडलॉक करेगा (हम क्यों पता लगाने की कोशिश कर रहे हैं) और अनुरोध कभी सफल नहीं होगा। चूंकि सर्वर सोचता है कि यह लाइव है, इसलिए यह सॉकेट कनेक्शन खोलता रहता है इस प्रकार एलडब्लूपी :: उपयोगकर्ताएजेंट का टाइमआउट मान हमें अब तक अच्छा नहीं करता है। अनुरोध पर पूर्ण टाइमआउट लागू करने का सबसे अच्छा तरीका क्या है?एलडब्लूपी पर सही समय समाप्ति :: UserAgent अनुरोध विधि

एफवाईआई, यह एक DNS समस्या नहीं है। डेडलॉक में एक ही समय में हमारे पोस्टग्रेज़ डेटाबेस को मारने वाले भारी संख्या में अद्यतनों के साथ कुछ करना है। परीक्षण उद्देश्यों के लिए, हमने अनिवार्य रूप से सर्वर प्रतिक्रिया हैंडलर में थोड़ी देर (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; 
    } 
} 
+0

संभावित डुप्लिकेट [पर्ल में एक निश्चित समय समाप्ति लागू करने के लिए कैसे?] (Http://stackoverflow.com/questions/15899855/how-to-enforce-a -definite-timeout-in-perl) – sixtyfootersdude

उत्तर

12

आप LWPx::ParanoidAgent, LWP :: UserAgent का एक उपवर्ग जो कि यह कैसे दूरस्थ वेबसर्वर साथ सूचना का आदान के बारे में और अधिक सतर्क है की कोशिश कर सकते।

अन्य चीजों के अलावा, यह आपको वैश्विक टाइमआउट निर्दिष्ट करने की अनुमति देता है। इसे लाइवजर्नल परियोजना के हिस्से के रूप में ब्रैड फिट्जपैट्रिक द्वारा विकसित किया गया था।

+0

यह टाइमआउट अभी भी DNS टाइमआउट – ryansstack

0

जो मैं समझता हूं, टाइमआउट संपत्ति खाते में DNS टाइमआउट नहीं लेती है। यह संभव है कि आप एक DNS लुकअप को अलग से बना सकें, फिर उपयोगकर्ता के लिए अनुरोध करें, यदि उपयोगकर्ता काम करता है, तो उपयोगकर्ता के लिए सही टाइमआउट मान सेट के साथ।

क्या यह सर्वर के साथ एक DNS समस्या है, या कुछ और?

संपादित करें: यह आईओ :: सॉकेट के साथ भी समस्या हो सकती है। अपने आईओ :: सॉकेट मॉड्यूल को अपडेट करने का प्रयास करें, और देखें कि क्या इससे मदद मिलती है। मुझे पूरा यकीन है कि वहां एक बग था जो एलडब्लूपी :: उपयोगकर्ता एजेन्ट टाइमआउट को काम करने से रोक रहा था।

एलेक्स

1

आप इस तरह अपने खुद के समय समाप्त कर सकते हैं:

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

से प्रभावित है, जब लोग 'जुड़ते हैं', <$fh> 'यह हमेशा मुझे खराब करता है - इनपुट में विभाजित करने के लिए अनावश्यक व्यस्त कार्य है, फिर उन लोगों से जुड़ें, इसके अलावा यह दो बार स्मृति लेता है। लिखें 'स्थानीय {/; <$fh>} 'इसके बजाए। –

+2

आप सही हैं, लेकिन मॉड्यूल का उपयोग करते समय मुझे बिल्कुल नहीं पता कि यह अंदर क्या कर रहा है, मैं इसे प्रदान करने के तरीकों का उपयोग करना पसंद करता हूं। आईओ :: पाइप अंदर/दूसरे मूल्य में सेट कर सकता है (यह स्पष्ट रूप से नहीं करता है, लेकिन यह हो सकता है)। इसके अलावा, इस बिंदु पर "डू" असफल है। ब्रेक एक नया दायरा शुरू करने के लिए पर्याप्त हैं। – jkramer

0

मूल जवाब में से एक के निम्नलिखित सामान्यीकरण भी पिछले हैंडलर के लिए अलार्म संकेत हैंडलर पुनर्स्थापित करता है और अलार्म के लिए दूसरी कॉल कहते हैं (0) में अगर eval घड़ी में कॉल एक अलार्म अपवाद फेंकता है और हम अलार्म रद्द करना चाहते हैं। इसके अलावा $ @ निरीक्षण और हैंडलिंग जोड़ा जा सकता है:

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