3

私が取り組んでいる単純なクローラーでperl のスレッドモジュールを使用しているので、ページを並行してダウンロードできます。時折、次のようなエラー メッセージが表示されます。

Thread 7 terminated abnormally: read timeout at /usr/lib64/perl5/threads.pm line 101.
Thread 15 terminated abnormally: Can't connect to burgundywinecompany.com:80 (connect: timeout) at /usr/lib64/perl5/threads.pm line 101.
Thread 19 terminated abnormally: write failed: Connection reset by peer at /usr/lib64/perl5/threads.pm line 101.

スレッドを使用せずにスクリプトを直線的に実行すると、これらのエラーは発生しません。そして、これらのエラーはほとんどLWP::UserAgentモジュールからのもののように見えますが、スレッドが異常終了する原因にはならないようです。perl のスレッドを使用する際に特に注意すべきことはありますか? ありがとう!

アップデート:

これらの異常終了の原因を突き止めましたが、LWP::UserAgent. メソッド呼び出しを削除して Web ページをダウンロードすると、エラーが停止します。

サンプルスクリプト

以下のスクリプトは、私が話している 1 つのエラーを引き起こします。最後の URL がタイムアウトし、HTTP::Repsonse オブジェクトの一部であるべきものが代わりにスレッドの異常終了を引き起こします。

#!/usr/bin/perl
use threads;
use Thread::Queue;
use LWP::UserAgent;

my $THREADS=10; # Number of threads
                             #(if you care about them)
my $workq = Thread::Queue->new(); # Work to do

my @stufftodo = qw(http://www.collectorsarmoury.com/ http://burgundywinecompany.com/ http://beetreeminiatures.com/);

$workq->enqueue(@stufftodo); # Queue up some work to do
$workq->enqueue("EXIT") for(1..$THREADS); # And tell them when

threads->create("Handle_Work") for(1..$THREADS); # Spawn our workers

$_->join for threads->list;

sub Handle_Work {
    while(my $todo=$workq->dequeue()) {
        last if $todo eq 'EXIT'; # All done
        print "$todo\n";
        my $ua = LWP::UserAgent->new;
        my $RESP = $ua->get($todo);
    }
    threads->exit(0);
}
4

3 に答える 3

3

私はあなたのソースで少し遊んで、これを思いつきました:

#!/usr/bin/perl

use 5.012; use warnings;
use threads; use Thread::Queue; use LWP::UserAgent;

use constant THREADS => 10;

my $queue = Thread::Queue->new();
my @URLs =  qw( http://www.collectorsarmoury.com/
                http://burgundywinecompany.com/
                http://beetreeminiatures.com/       );
my @threads;

for (1..THREADS) {
    push @threads, threads->create(sub {
        my $ua = LWP::UserAgent->new;
        $ua->timeout(5); # short timeout for easy testing.
        while(my $task = $queue->dequeue) {
            my $response = eval{ $ua->get($task)->status_line };
            say "$task --> $response";
        }
    });
}

$queue->enqueue(@URLs);
$queue->enqueue(undef) for 1..THREADS;
# ... here work is done
$_->join foreach @threads;

出力:

http://www.collectorsarmoury.com/ --> 200 OK
http://burgundywinecompany.com/ --> 200 OK
http://beetreeminiatures.com/ --> 500 Can't connect to beetreeminiatures.com:80 (timeout)

なしで出力eval:

http://www.collectorsarmoury.com/ --> 200 OK
http://burgundywinecompany.com/ --> 200 OK
http://beetreeminiatures.com/ --> 500 Can't connect to beetreeminiatures.com:80 (timeout)
Thread 2 terminated abnormally: Can't connect to beetreeminiatures.com:80 (timeout)

LWP::Protocol::http::Socket: connect: timeout at /usr/share/perl5/LWP/Protocol/http.pm line 51.

私が違うことをすることは次のとおりです。

重要でない:

  • exit私は私のスレッドではありません。最後にドロップするだけです(暗黙的return
  • リクエストごとに 1 つではなく、スレッドごとに 1 つのユーザー エージェントを割り当てます。

より良いスタイル:

  • 私はundefスレッドの終了を通知するために使用します。偽の値がキューから取り出されると、ループ条件はとにかく偽になり、スレッドは終了します。特別な文字列を渡して終了を通知したい場合は、 でループしwhile (1)、ループ本体内でキューから取り出す必要があります。

重要:

  • これらの厄介なエラーを黙らせるために、私evalget. リクエストがあった場合die、私のスレッドは従いませんが、落ち着いて続行します。

getURL を変更すると、実際に死ぬ可能性があるためです。LWP::Protocol::httpのソースの 51 行目を見ると、接続用のソケットを作成できない場合に致命的なエラーが発生することがわかります。これは、ホスト名を解決できない場合に発生する可能性があります。

私のコードでは、エラーを無視することにしました (ステータス行を既に出力しているため)。問題によっては、URL を再試行するか、より有益な警告を表示する必要がある場合があります。エラー処理の良い例については、リンクされたソースを参照してください。

残念ながら、正確なエラーを再現できませんでした (警告で指定された行は、threads->exit()クラス メソッドを指しています)。ただし、ほとんどの場合、eval を使用すると、異常終了を防ぐことができます。

于 2012-11-18T13:03:59.667 に答える
2

getメソッドが設定されていないように見えますが、設定$@されていませんdie。の後にいくつかのプリントを配置することで、それが死んでいないことがわかりますget

my $RESP = $ua->get($todo);
if($RESP->is_success) {
    print "$todo success\n";
} else {
    print "$todo failed: ".$RESP->status_line."\n";
}

スレッドが終了する前に、失敗したリクエストがまだ発生した後の出力を確認できます。

http://www.collectorsarmoury.com/ success
http://burgundywinecompany.com/ success
http://beetreeminiatures.com/ failed: 500 Can't connect to beetreeminiatures.com:80 (Connection timed out)
Thread 3 terminated abnormally: Can't connect to beetreeminiatures.com:80 (Connection timed out)

スレッドの出口は$@、異常として設定されているように見えます。$@スレッドを終了する前に (またはlocal $@Handle_Workまたはevalの周りで)リセットすると、スレッドは正常に終了しgetます。

于 2012-11-18T13:05:00.163 に答える
0

よくperlには、fatal()を中止して実行するメカニズムがあります。しかし、私はこれがあなたには当てはまらないと思います。

threads.plの101行目を見ると、これはおそらくスレッドの終了メソッドであり、ゼロ以外の終了ステータスで使用すると異常な状態と見なされる可能性があります。

これらは無害であり、「異常終了」の使用は、操作が100%成功しなかったことを示しているにすぎないと思います。これは、操作が完了しなかったスレッドのリカバリシナリオを計画および実装する必要があることを意味します。

あなたにとって、言葉の選択は憂慮すべきものであり、懸念を引き起こしますが、メッセージを次のように変更すると、「スレッド123は成功を示して完了しませんでした」と思われるかもしれません。

また、スレッドのmainメソッドが返されるようにすることをお勧めします(必要に応じて途中でデータの割り当てを解除します)。もちろん、これがmainメソッドの最後の処理として実行されている場合を除き、これはthreads::exitを使用する代わりになります。

フォークに関して、フォーク時に失敗することはなく、フォークされたプロセスがゼロ以外の「終了ステータス」での失敗を示していると主張していますか。また、スレッドを使用するときに、Webサイト、プロキシ、ネットワークなどに過負荷がかかっていないことを確認してください。

于 2012-11-18T08:59:08.077 に答える