【问题标题】:Reusing the same curl handle. Big performance increase?重复使用相同的 curl 句柄。性能大幅提升?
【发布时间】:2011-04-16 18:21:58
【问题描述】:

在一个 PHP 脚本中,我对不同的 URL 执行了很多不同的 curl GET 请求(一百个)。

重用来自curl_init 的相同句柄会提高性能,还是与请求的响应时间相比可以忽略不计?

我之所以这么问,是因为在当前架构中,保持相同的句柄并不容易。

【问题讨论】:

  • 你看过curl_multi_init吗?
  • 是的,但我需要做同步 curl 请求。
  • 小心使用这个!请参阅下面我的回答中的警告

标签: php curl


【解决方案1】:

Should I close cURL or not? 交叉发布,因为我认为它在这里也很相关。

我尝试通过为每个请求使用新句柄并使用具有以下代码的相同句柄来代替 curl:

ob_start(); //Trying to avoid setting as many curl options as possible
$start_time = microtime(true);
for ($i = 0; $i < 100; ++$i) {
    $rand = rand();
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "http://www.google.com/?rand=" . $rand);
    curl_exec($ch);
    curl_close($ch);
}
$end_time = microtime(true);
ob_end_clean();
echo 'Curl without handle reuse: ' . ($end_time - $start_time) . '<br>';

ob_start(); //Trying to avoid setting as many curl options as possible
$start_time = microtime(true);
$ch = curl_init();
for ($i = 0; $i < 100; ++$i) {
    $rand = rand();
    curl_setopt($ch, CURLOPT_URL, "http://www.google.com/?rand=" . $rand);
    curl_exec($ch);
}
curl_close($ch);
$end_time = microtime(true);
ob_end_clean();
echo 'Curl with handle reuse: ' . ($end_time - $start_time) . '<br>';

得到以下结果:

无手柄重复使用的卷曲:8.5690529346466
带手柄重用卷曲:5.3703031539917

因此,当多次连接到同一个服务器时,重复使用同一个句柄实际上可以显着提高性能。我尝试连接到不同的服务器:

$url_arr = array(
    'http://www.google.com/',
    'http://www.bing.com/',
    'http://www.yahoo.com/',
    'http://www.slashdot.org/',
    'http://www.stackoverflow.com/',
    'http://github.com/',
    'http://www.harvard.edu/',
    'http://www.gamefaqs.com/',
    'http://www.mangaupdates.com/',
    'http://www.cnn.com/'
);
ob_start(); //Trying to avoid setting as many curl options as possible
$start_time = microtime(true);
foreach ($url_arr as $url) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_exec($ch);
    curl_close($ch);
}
$end_time = microtime(true);
ob_end_clean();
echo 'Curl without handle reuse: ' . ($end_time - $start_time) . '<br>';

ob_start(); //Trying to avoid setting as many curl options as possible
$start_time = microtime(true);
$ch = curl_init();
foreach ($url_arr as $url) {
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_exec($ch);
}
curl_close($ch);
$end_time = microtime(true);
ob_end_clean();
echo 'Curl with handle reuse: ' . ($end_time - $start_time) . '<br>';

得到以下结果:

没有手柄重用的卷曲:3.7672290802002
带手柄重用卷曲:3.0146431922913

仍然有相当大的性能提升。

【讨论】:

  • 出于好奇,第二个测试中的“rand()”调用是做什么的?似乎这可能会在所比较的基准之间引入实质性差异。
  • @drobert 好点。在第二次测试中不需要它。但由于第二次测试只有 10 次迭代,而且我们处理的是几秒钟内的时间,所以它的影响并不大。
【解决方案2】:

这取决于网址是否在同一台服务器上。如果是,对同一服务器的并发请求将重用该连接。请参阅 CURLOPT_FORBID_REUSE。

如果 url 有时在同一台服务器上,您需要对 url 进行排序,因为默认连接缓存限制为 10 或 20 个连接。

如果它们位于不同的服务器上,则使用相同的句柄没有速度优势。

使用 curl_multi_exec,您可以同时(并行)连接到不同的服务器。即使这样,您也需要排队才能不使用数千个同时连接。

【讨论】:

  • 这个答案模棱两可。它没有明确回答用户的问题 - 是否重复使用相同的 curl 句柄...提高性能?和语句“如果它们是,对同一服务器的并发请求将重用连接。”
  • 同意@JohnnyWong。
  • 对第一句更准确的改进应该是:It depends on if the urls are on same servers or not. If they are, concurrent requests to same server will reuse the connection, **if same curl handle is reused**. see CURLOPT_FORBID_REUSE.
【解决方案3】:

我有一个类似的场景,我将数据发布到服务器。它被分成约 100 行的请求,因此会产生大量请求。在基准测试中,我比较了 12.614 行(需要 127 个请求)加上身份验证和另一个内务处理请求(总共 129 个请求)的两种方法。

请求通过网络发送到同一国家/地区的服务器,而不是现场。它们受 TLS 1.2 保护(握手也会造成损失,但鉴于 HTTPS 越来越成为默认选择,这甚至可能使其更类似于您的场景)。

使用 cURL 重用: 一个$curlHandle,即curl_init()'ed 一次,然后只用CURLOPT_URLCURLOPT_POSTFIELDS 修改

Run  1: ~42.92s
Run  3: ~41.52s
Run  4: ~53.17s
Run  5: ~53.93s
Run  6: ~55.51s
Run 11: ~53.59s
Run 12: ~53.76s
Avg: 50,63s / Std.Dev: 5,8s
TCP-Conversations / SSL Handshakes: 5 (Wireshark)

没有 cURL 重用: 每个请求一个curl_init

Run  2: ~57.67s
Run  7: ~62.13s
Run  8: ~71.59s
Run  9: ~70.70s
Run 10: ~59.12s
Avg: 64,24s / Std. Dev: 6,5s
TCP-Conversations / SSL Handshakes: 129 (Wireshark)

它不是最大的数据集,但可以说所有“重用”运行都比所有“init”运行快。平均时间相差近 14 秒。

【讨论】:

  • 非常有趣。
【解决方案4】:

这取决于您将发出多少个请求 - 关闭和重新打开每个请求的开销可以忽略不计,但是当做一千个请求时呢?可能需要几秒钟或更长时间。

我相信 curl_multi_init 会是最快的方法。

整个事情取决于你需要做多少请求。

【讨论】:

  • 我不能使用 curl_multi_init 因为我的 curl 请求需要同步。我每次都会有一百个请求。
【解决方案5】:

也看看这个

尝试 { $pool = 新的 HttpRequestPool( 新的 HttpRequest($q1), 新的 HttpRequest($qn) ); $池->发送(); foreach($pool as $request) { $out[] = $request->getResponseBody(); } } 捕捉(HttpException $e){ 回声$e; }

【讨论】:

  • 我看不出你的回答与我的问题有什么关系......你能更准确一点吗?
  • 好吧,这是解决问题的不同方法。如果你需要大量的 curl GET 请求,你可以使用专门为此目的设计的 php 的 HttpRequestPool:pecl.php.net/package/pecl_http
【解决方案6】:

虽然这个问题得到了正确回答,但我想添加一个 警告 以不将 curl 重新用于 POST 或 PUT 请求,因为重置并不总是完全完成。

我刚刚遇到以下问题,导致我的数据库中的数据损坏。 :-(

由于某些记录中的一些损坏的 ascii 代码,request-post 仍然是空的,我的脚本没有检查:-((我当然会解决这个问题) curl 似乎有来自先前记录的请求发布,并且只是传递了它。没有返回错误。

如果 curl 为每个请求都初始化,就不会发生这种情况。在这种情况下,将没有任何可用的预加载数据,因此服务器将响应空错误。

所以我的建议是,safefast 更好:总是使用 new curl 实例,除了获取外部数据 em>。

更新: 我刚刚发现我没有使用 php-function curl_reset()。根据将重置一切的手册。现在我更喜欢使用curl_close()curl_init() ;-)

我希望我解释得足够好,如果不清楚,请询问! 问候

【讨论】:

    猜你喜欢
    • 2015-10-17
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-24
    相关资源
    最近更新 更多