首先,我是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请求吗?
答案 0 :(得分: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
将允许线程退出。此时,线程将通过并处理工作,您将通过最后的连接收集响应。
答案 1 :(得分:0)
每当我需要异步解决方案Perl时,我首先看一下POE框架。在这种特殊情况下,我使用了POE HTTP Request module,它允许我们同时发送多个请求,并提供一个回调机制,您可以在其中处理您的http响应。
Perl线程很可怕并且可能会使您的应用程序崩溃,尤其是当您加入或分离它们时。如果响应不需要很长时间来处理,那么单线程POE解决方案可以很好地工作。
有时候,我们必须依赖线程,因为应用程序因长时间运行而被阻止。在这些情况下,我在启动应用程序中的任何内容之前创建了一定数量的线程。然后使用Thread :: Queue将数据从主线程传递给这些工作者并且永远不会加入/分离它们;为了稳定目的,始终保持它们。 (对于每种情况都不是理想的解决方案。)
POE现在支持线程,每个线程都可以运行POE :: Kernel。内核可以通过TCP套接字相互通信(POE提供了很好的解锁接口)。