PHP并发请求优化:使用`curl_multi_select()`实现高效的多请求处理

PHP并发请求优化:使用curl_multi_select()实现高效的多请求处理

背景

最近在项目中遇到一个需求,需要从多个 1 级网站(超过 200 个)获取数据,并且是通过 POST 请求瞬间发送到这些网站上。开始时我直接使用了 curl_exec() 逐一发送请求,但是很快就发现了一个问题:这个方法是阻塞式的,只能一次发送一个请求。处理 200 多个请求时,性能非常差。显然,这种方法不适合大规模的并发请求。

为了解决这个问题,我查阅了大量资料,最后找到了一个不错的方案——使用 curl_multi_exec()curl_multi_select() 实现并发请求。这样不仅能大幅提升性能,还能避免 CPU 资源的浪费。下面是我的经验分享,希望对大家有帮助。

1. 为什么不用 curl_exec()

最开始,我像往常一样使用 curl_exec() 来发送请求,但是遇到了以下两个问题:

  1. 性能低下:每次只能处理一个请求,多个请求只能串行执行,耗时较长。
  2. 资源消耗大:频繁的网络请求导致 CPU 长时间空转,系统资源被大量占用。

显然,这样的方式不适合处理 200 多个并发请求,尤其是当项目需要高效、实时获取数据的时候。所以我需要找到一种更好的方式来实现并发请求。

2. 使用 curl_multi_exec() 实现并发请求

为了提升并发请求的效率,我决定使用 curl_multi_exec()。它可以将多个 cURL 请求组合在一起并行执行,从而大大提高了处理效率。

基本使用方法

首先,简单介绍一下如何使用 curl_multi_exec()。这个函数允许同时执行多个 cURL 请求,而不需要等待每个请求的完成。

$multiCurl = curl_multi_init();  // 初始化multi cURL句柄

// 创建单个cURL请求
$ch1 = curl_init('https://example.com');
$ch2 = curl_init('https://example2.com');

// 将单个cURL句柄加入multi句柄中
curl_multi_add_handle($multiCurl, $ch1);
curl_multi_add_handle($multiCurl, $ch2);

// 执行并发请求
do {
    $status = curl_multi_exec($multiCurl, $active);
} while ($active && $status == CURLM_OK);

// 关闭cURL句柄
curl_multi_remove_handle($multiCurl, $ch1);
curl_multi_remove_handle($multiCurl, $ch2);
curl_multi_close($multiCurl);

通过这种方法,我可以同时发送多个请求,而不必等待每个请求逐一完成。对比 curl_exec(),处理多个请求的效率提高了很多。

3. 优化:结合 curl_multi_select() 降低系统资源消耗

虽然 curl_multi_exec() 可以并发处理多个请求,但它本身并不会阻塞,因此我发现频繁调用它会导致系统的 CPU 资源大量消耗。这个问题在处理大量请求时尤其明显。

后来经过一番摸索,发现了 curl_multi_select()。这个函数可以阻塞进程,直到有请求完成或发生变化(如接收到数据等)。这样可以减少不必要的 CPU 占用,让系统只在有必要时才执行检查。

curl_multi_select() 的用法

通过 curl_multi_select(),我能有效减少 CPU 的空转等待。它的工作方式很简单:在等待时段内,如果有请求状态发生变化(比如完成、错误等),它就会继续执行。如果没有变化,进程会阻塞直到超时。

$multiCurl = curl_multi_init();

// 创建多个cURL请求
$ch1 = curl_init('https://example.com');
$ch2 = curl_init('https://example2.com');

// 添加请求句柄
curl_multi_add_handle($multiCurl, $ch1);
curl_multi_add_handle($multiCurl, $ch2);

do {
    $status = curl_multi_exec($multiCurl, $active);
    
    if ($active) {
        // 使用 curl_multi_select 等待请求状态的变化,避免频繁CPU循环
        curl_multi_select($multiCurl);
    }

} while ($active && $status == CURLM_OK);

// 获取并处理每个请求的响应
curl_multi_remove_handle($multiCurl, $ch1);
curl_multi_remove_handle($multiCurl, $ch2);
curl_multi_close($multiCurl);

在这段代码中,curl_multi_select() 用于等待活动发生,只有当请求完成或者有数据时,才会继续执行,节省了 CPU 资源。

4. 实际应用:批量并发请求

为了处理项目中的大规模并发请求,我最终实现了一个可以同时发送多个请求的批处理脚本。以下是完整代码:



// 初始化multi cURL句柄
$multiCurl = curl_multi_init();
$curlHandles = [];

// 准备多个URL
$urls = [
    'https://example.com',
    'https://example2.com',
    'https://example3.com',
    'https://example4.com'
];

// 创建cURL句柄并将其添加到multi句柄中
foreach ($urls as $i => $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回结果而不是直接输出
    $curlHandles[$i] = $ch;
    curl_multi_add_handle($multiCurl, $ch);
}

do {
    $status = curl_multi_exec($multiCurl, $active);
    
    if ($active) {
        // 阻塞等待直到有请求完成
        curl_multi_select($multiCurl);
    }
    
} while ($active && $status == CURLM_OK);

// 获取并处理每个请求的响应
foreach ($curlHandles as $i => $ch) {
    $response = curl_multi_getcontent($ch);
    echo "Response from URL {$urls[$i]}: $response\n";
    curl_multi_remove_handle($multiCurl, $ch);  // 移除句柄
}

// 关闭multi句柄
curl_multi_close($multiCurl);

通过这个方法,我成功实现了向 200 多个 1 级网站同时发起 POST 请求的需求,速度比最初的逐一请求快了很多倍。而且系统资源消耗也得到了很好的控制,避免了 CPU 的过度占用。

5. 总结

通过使用 curl_multi_exec()curl_multi_select(),我们可以大幅提升 PHP 中并发请求的处理效率,特别是当需要处理大量外部请求时。这种方法不仅能提高响应速度,还能有效降低系统资源的消耗,是非常适合大规模并发处理的解决方案。

希望这篇文章能为大家提供一些帮助,如果有类似需求的开发者也可以尝试这个方法。有什么问题或建议,欢迎留言交流!

你可能感兴趣的:(php,开发语言)