سؤال

أحاول تنفيذ طلب إلى خادم غير موثوق به.من الجيد الحصول على الطلب، ولكنه ليس مطلوبًا بنسبة 100% لإكمال البرنامج النصي الخاص بـ Perl بنجاح.تكمن المشكلة في أن الخادم قد يصل إلى طريق مسدود أحيانًا (نحن نحاول معرفة السبب) ولن ينجح الطلب أبدًا.نظرًا لأن الخادم يعتقد أنه مباشر، فإنه يبقي اتصال المقبس مفتوحًا وبالتالي فإن قيمة المهلة الخاصة بـ 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 وهي أكثر حذرًا بشأن كيفية تفاعلها مع خوادم الويب البعيدة.

من بين أمور أخرى، فإنه يسمح لك بتحديد مهلة عالمية.تم تطويره بواسطة براد فيتزباتريك كجزء من مشروع 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";
}   

مما أفهمه، خاصية المهلة لا تأخذ في الاعتبار مهلات DNS.من الممكن أن تقوم بإجراء بحث DNS بشكل منفصل، ثم تقديم الطلب إلى الخادم إذا نجح ذلك، مع تعيين قيمة المهلة الصحيحة لوكيل المستخدم.

هل هذه مشكلة DNS مع الخادم، أو أي شيء آخر؟

يحرر:من الممكن أيضًا أن تكون هناك مشكلة في IO::Socket.حاول تحديث وحدة IO::Socket الخاصة بك، ومعرفة ما إذا كان ذلك يساعد.أنا متأكد تمامًا من وجود خطأ ما يمنع LWP::UserAgent من العمل.

أليكس

يؤدي التعميم التالي لإحدى الإجابات الأصلية أيضًا إلى استعادة معالج إشارة الإنذار إلى المعالج السابق وإضافة مكالمة ثانية إلى التنبيه (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;
     }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top