PHP实现并发请求:单线程循环与Curl多句柄对比

小岛来信
小岛来信
发布时间:2026-10-13| 5人浏览过
原创

PHP虽然自身不支持原生多线程,但可以通过cURL扩展中的`curl_multi_*`系列函数实现高效的并发请求,或通过简单的循环模拟并发效果。本文将详细解析这两种方法的实现原理、代码结构及性能差异,帮助用户在实际开发中进行合理选型。

核心结论与适用场景

在需要从多个URL同时获取数据(如批量采集图片、API聚合)的场景下,PHP的处理方式主要取决于对速度和代码复杂度的权衡:

  • 单线程循环(Sequential):代码极简,适合数量较少(如10个以内)或网络延迟极低的场景。由于是串行执行,总耗时等于所有请求耗时之和,且容易出现资源占用时间长的问题。
  • 多句柄并发(Multi-Handle):利用curl_multi_init()建立管道,异步执行请求。这是PHP中标准的“伪多线程”实现,能大幅降低总耗时,接近各请求中最慢的一个,是处理大量并发任务的推荐方案。

方法一:基于Foreach的单线程串行请求

这是最直观的实现方式。通过foreach遍历URL数组,逐个初始化cURL句柄、执行并关闭。需要注意的是,必须确保在循环结束后再关闭最后一个全局变量,或者在每次循环内完整闭环。

第1步:构建基础循环逻辑

目的:按顺序获取各个URL的数据。

入口:定义目标URL数组。
动作:遍历数组,为每个URL创建独立的cURL会话。
参数:需设置CURLOPT_RETURNTRANSFER以获取返回内容而非直接输出。

$urls = ['http://example.com/a', 'http://example.com/b'];

foreach ($urls as $key => $url) {
    // 1. 初始化cURL会话
    $ch = curl_init($url);
    
    // 2. 配置选项
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回数据而非直接输出
    curl_setopt($ch, CURLOPT_BINARYTRANSFER, true); // 兼容旧版本二进制数据传输
    
    // 3. 执行请求
    $data = curl_exec($ch);
    
    // 4. 记录结果
    echo "[$key]: " . $data . "\n";
    
    // 5. 【关键】在当前循环迭代内关闭句柄
    curl_close($ch);
}

常见错误提醒:如果将closel_close($ch)放在循环体外,且$ch是外部变量,可能导致后续迭代覆盖前一个句柄引用,引发内存泄漏或连接复用混乱。上述代码在循环内执行关闭是最安全的做法。

方法二:使用 Curl Multi Handle 实现高效并发

PHP官方推荐的并发处理方式是利用curl_multi接口。它可以在单个进程中管理多个cURL句柄,让网络I/O并行发生,从而显著提升整体执行效率。

第1步:初始化与管理多句柄环境

目的:创建容器并添加多个请求任务。

入口:调用curl_multi_init()生成句柄组。
动作:遍历URL列表,为每个URL初始化独立cURL句柄,并通过curl_multi_add_handle()添加到多句柄容器中。

$mh = curl_multi_init();
$curl_array = array();
$nodes = [
    'https://www.baidu.com',
    'https://www.google.com'
];

// 注册所有请求
foreach ($nodes as $i => $url) {
    $curl_array[$i] = curl_init($url);
    curl_setopt($curl_array[$i], CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($mh, $curl_array[$i]);
}

第2步:执行并发请求轮询

目的:等待所有网络请求完成。

入口:使用curl_multi_exec()配合do...while循环。
动作:持续执行直到所有句柄的状态标志位$running变为0。期间加入usleep(10000)(10毫秒)以避免CPU空转消耗过高资源。

$running = null;
do {
    usleep(10000); // 避免CPU高负载
    curl_multi_exec($mh, $running);
} while ($running > 0);

第3步:获取结果并清理资源

目的:提取数据并释放内存。

入口:curl_multi_getcontent()和清理函数。
动作:遍历原数组,从每个对应的多句柄中提取内容。最后务必移除所有句柄并关闭多句柄组,防止内存泄露。

$res = array();
foreach ($nodes as $i => $url) {
    $res[$url] = curl_multi_getcontent($curl_array[$i]);
}

// 清理阶段
foreach ($nodes as $i => $url) {
    curl_multi_remove_handle($mh, $curl_array[$i]);
}
curl_multi_close($mh);

print_r($res);

性能与注意事项总结

尽管curl_multi被称为“伪多线程”,因为它并未创建操作系统级别的线程,而是通过IO多路复用技术模拟并发,但其实际吞吐量远优于单线程循环。在处理超过10个以上URL请求时,建议使用curl_multi方案。

  • 超时控制:在生产环境中,建议为所有cURL请求设置CURLOPT_TIMEOUT,以防止因某个服务器无响应导致程序永久挂起。
  • 并发限制:虽然并发越快越好,但过高的并发可能触发目标服务器的限流机制(如HTTP 429)。如需更复杂的调度,可结合令牌桶算法控制并发数。
  • 兼容性:CURLOPT_BINARYTRANSFER在较新的PHP版本中已不再必需,仅用于保持向后兼容,现代开发中可直接省略。

以上就是PHP中利用Curl实现并发请求的详细方法与优化技巧,更多关于PHP网络编程的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。