【问题标题】:Perl parallel HTTP requests - out of memoryPerl 并行 HTTP 请求 - 内存不足
【发布时间】:2012-10-24 15:03:29
【问题描述】:

首先,我是 Perl 的新手。 我想在 Perl 的 REST API 上发出多个(例如 160 个)HTTP GET 请求。一个接一个地执行它们需要很长时间,所以我正在考虑并行运行这些请求。因此我使用线程同时执行更多请求,并将并行请求的数量限制为 10。 这在我第一次运行程序时工作得很好,在第 40 个请求之后我第二次运行“内存不足”。

代码如下:(@urls 包含请求的 160 个 URL)

while(@urls) {
  my @threads;
  for (my $j = 0; $j < 10 and @urls; $j++) {
    my $url = shift(@urls);
    push @threads, async { $ua->get($url) };
  }

  for my $thread (@threads) {
  my $response = $thread->join;
  print "$response\n"; 
 }
}

所以我的问题是,为什么我不是第一次用完内存,而是第二次用完(我的代码中是否遗漏了一些重要的东西)?我能做些什么来防止它? 还是有更好的方法来执行并行 GET 请求?

【问题讨论】:

    标签: multithreading perl


    【解决方案1】:

    我不确定为什么在第一次运行时没有得到 OOM 错误,而在第二次运行时会出现 OOM 错误;当您运行 Perl 脚本并且 perl 二进制文件退出时,它会将所有内存释放回操作系统。执行之间没有任何保留。 REST 服务每次返回的数据是否完全相同?也许你第二次跑的时候有更多的数据,它把你推到了边缘。

    我注意到的一个问题是,您要启动 10 个线程并运行它们以完成,然后再生成 10 个线程。更好的解决方案可能是工作线程模型。在程序开始时生成 10 个线程(或任意数量),将 URL 放入队列中,并允许线程自己处理队列。这是一个可能有帮助的简单示例:

    use strict;
    use warnings;
    use threads;
    use Thread::Queue;
    
    my $q = Thread::Queue->new();
    
    my @thr = map {
        threads->create(sub {
            my @responses = ();
            while (defined (my $url = $q->dequeue())) {
                push @responses, $ua->get($url);
            }
            return @responses;
        });
    } 1..10;
    
    $q->enqueue($_) for @urls;
    $q->enqueue(undef) for 1..10;
    
    foreach (@thr) {
        my @responses_of_this_thread = $_->join();
        print for @responses_of_this_thread;
    }
    

    请注意,我尚未对此进行测试以确保其有效。在此示例中,您创建了一个新线程队列并产生了 10 个工作线程。每个线程都会阻塞 dequeue 方法,直到有东西要读取。接下来,将您拥有的所有 URL 和每个线程的 undef 排队。 undef 将允许线程在没有更多工作要执行时退出。此时,线程将通过并处理工作,您将通过最后的连接收集响应。

    【讨论】:

    • 为了回答您的问题,REST 服务每次都返回完全相同的数据。我试过你的代码,我不得不改变一行:my @thr = map { threads-&gt;create( sub { my $url = $q-&gt;dequeue(); return unless defined $url; $ua-&gt;get($url)}) } 1..10;这只执行10个请求还是我又犯了一个错误?但是,有时它会完成所有 10 个请求,有时我会收到 OOM 错误。
    • @user1771548 Joel 在快速编码时犯了一个小错误。我更新了代码,它现在循环 inside 线程,应该解决这部分问题。不过,没有关于内存问题的想法。
    • @user1771548 你在为$ua 使用什么模块?快速搜索 LWP 和线程安全会发现一些线程提到了潜在的问题。您是否考虑过为此使用 LWP::Parallel(如果您还没有)?
    • @user1771548 另外,您使用的是什么版本的 Perl (perl -v),您在什么平台上运行它?
    • @Joel 我正在使用 LWP::UserAgent,还没有看过 LWP::Parallel。版本为5.16.1,平台为Windows7,32bit。
    【解决方案2】:

    每当我需要 Perl 的异步解决方案时,我都会首先查看 POE 框架。在这种特殊情况下,我使用了POE HTTP Request module,这将允许我们同时发送多个请求并提供一个回调机制,您可以在其中处理您的 http 响应。

    Perl 线程很可怕,可能会使您的应用程序崩溃,尤其是当您加入或分离它们时。如果响应不需要很长时间来处理,单线程 POE 解决方案就可以很好地工作。

    但有时,我们不得不依赖线程,因为应用程序由于长时间运行的任务而被阻塞。在这些情况下,我会在应用程序中启动任何内容之前创建一定数量的线程。然后使用 Thread::Queue 我将数据从主线程传递给这些工作人员,并且从不加入/分离它们;出于稳定目的,请始终将它们放在身边。 (不是每种情况的理想解决方案。)

    POE 现在支持线程,每个线程都可以运行一个 POE::Kernel。内核可以通过 TCP 套接字相互通信(POE 提供了很好的解锁接口)。

    【讨论】:

    • 对于这样一个简单的问题,引入所有 POE 的东西可能是矫枉过正。此外,还有学习所有额外 API 的问题。一旦你了解了陷阱,Perl 线程就会非常优雅。
    • 现在,IMO,AnyEvent 是一个更好的框架,更容易使用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多