3

首先,我是 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 请求?

4

2 回答 2

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当没有更多工作要执行时,这将允许线程退出。此时,线程将通过并处理工作,最后您将通过连接收集响应。

于 2012-10-24T15:28:43.830 回答
0

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

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

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

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

于 2012-10-24T20:32:32.803 回答