我必须编写一个脚本,以并行获取一些URL并进行一些工作。过去,我总是使用Parallel::ForkManager进行此类操作,但现在我想学习一些新知识,并尝试使用AnyEvent(以及AnyEvent::HTTPAnyEvent::Curl::Multi)进行异步编程...但是我在理解AnyEvent并编写应满足以下条件的脚本时遇到了问题:

  • 打开文件(每一行都是单独的URL)
  • (从现在开始并行执行,但限制为10个并发请求)
  • 逐行读取文件(我不想将整个文件加载到内存-可能很大)
  • 对该URL发出HTTP请求
  • 读取响应
  • 相应地更新MySQL记录
  • (下一行)

  • 我已经阅读了许多手册和教程,但是对于我来说,仍然很难理解阻塞代码和非阻塞代码之间的区别。我在http://perlmaven.com/fetching-several-web-pages-in-parallel-using-anyevent上找到了类似的脚本,Szabo先生在其中解释了基础知识,但我仍然不明白如何实现类似以下内容的脚本:
    ...
    open my $fh, "<", $file;
    while ( my $line = <$fh> )
    {
    # http request, read response, update MySQL
    }
    close $fh
    ...
    

    ...并在这种情况下添加并发限制。

    我将非常感谢您的帮助;)

    更新

    按照池上的建议,我尝试了Net::Curl::Multi。我对结果感到非常满意。经过多年使用Parallel::ForkManager并发抓取成千上万个URL之后,Net::Curl::Multi似乎很棒。
    这是我的文件句柄上带有while循环的代码。它似乎应该可以工作,但是考虑到这是我第一次编写类似的东西,我想请经验丰富的Perl用户看一下,并告诉我是否存在一些潜在的错误,我错过的东西等。
    另外,如果我可能要问:由于我不完全了解Net::Curl::Multi的并发性如何工作,请告诉我是否应该在将MySQL UPDATE命令(通过DBI)放入RESPONSE循环中时遇到任何问题(显然服务器负载会更高-我预计最终脚本将使用约50个并发的N::C::M worker运行,也许更多)。
    #!/usr/bin/perl
    
    use Net::Curl::Easy  qw( :constants );
    use Net::Curl::Multi qw( );
    
    sub make_request {
        my ( $url ) = @_;
        my $easy = Net::Curl::Easy->new();
        $easy->{url} = $url;
        $easy->setopt( CURLOPT_URL,        $url );
        $easy->setopt( CURLOPT_HEADERDATA, \$easy->{head} );
        $easy->setopt( CURLOPT_FILE,       \$easy->{body} );
        return $easy;
    }
    
    my $maxWorkers = 10;
    
    my $multi = Net::Curl::Multi->new();
    my $workers = 0;
    
    my $i = 1;
    open my $fh, "<", "urls.txt";
    LINE: while ( my $url = <$fh> )
    {
        chomp( $url );
        $url .= "?$i";
        print "($i) $url\n";
        my $easy = make_request( $url );
        $multi->add_handle( $easy );
        $workers++;
    
        my $running = 0;
        do {
            my ($r, $w, $e) = $multi->fdset();
            my $timeout = $multi->timeout();
            select $r, $w, $e, $timeout / 1000
            if $timeout > 0;
    
            $running = $multi->perform();
            RESPONSE: while ( my ( $msg, $easy, $result ) = $multi->info_read() ) {
                $multi->remove_handle( $easy );
                $workers--;
                printf( "%s getting %s\n", $easy->getinfo( CURLINFO_RESPONSE_CODE ), $easy->{url} );
            }
    
            # dont max CPU while waiting
            select( undef, undef, undef, 0.01 );
        } while ( $workers == $maxWorkers || ( eof && $running ) );
        $i++;
    }
    close $fh;
    

    最佳答案

    Net::Curl是一个相当不错的库,它的运行速度非常快。此外,它也可以处理并行请求!我建议使用它而不是AnyEvent。

    use Net::Curl::Easy  qw( :constants );
    use Net::Curl::Multi qw( );
    
    sub make_request {
        my ( $url ) = @_;
        my $easy = Net::Curl::Easy->new();
        $easy->{url} = $url;
        $easy->setopt( CURLOPT_URL,        $url );
        $easy->setopt( CURLOPT_HEADERDATA, \$easy->{head} );
        $easy->setopt( CURLOPT_FILE,       \$easy->{body} );
        return $easy;
    }
    
    my $max_running = 10;
    my @urls = ( 'http://www.google.com/' );
    
    my $multi = Net::Curl::Multi->new();
    my $running = 0;
    while (1) {
        while ( @urls && $running < $max_running ) {
           my $easy = make_request( shift( @urls ) );
           $multi->add_handle( $easy );
           ++$running;
        }
    
        last if !$running;
    
        my ( $r, $w, $e ) = $multi->fdset();
        my $timeout = $multi->timeout();
        select( $r, $w, $e, $timeout / 1000 )
            if $timeout > 0;
    
        $running = $multi->perform();
        while ( my ( $msg, $easy, $result ) = $multi->info_read() ) {
            $multi->remove_handle( $easy );
            printf( "%s getting %s\n", $easy->getinfo( CURLINFO_RESPONSE_CODE ), $easy->{url} );
        }
    }
    

    10-05 22:44