
浅析PHP如何并行异步处理HTTP请求
在PHP传统的同步阻塞模式下,当我们需要请求多个外部接口时,通常是串行执行的:发起请求A -> 等待响应 -> 发起请求B -> 等待响应。如果每个请求耗时1秒,3个请求就需要至少3秒。这在高并发或微服务架构下是不可接受的。
为了提升性能,我们可以采用并行异步处理的方式来同时发起多个HTTP请求,从而将总耗时降至最慢那个请求的耗时。本文将详细分析PHP中实现并行异步HTTP请求的三种主流方案。
一、原生cURL Multi方案
PHP内置的cURL扩展提供了curl_multi_*系列函数,这是最基础且无需安装额外依赖的并行处理方案。其核心原理是将多个cURL句柄添加到一个批处理会话中,由cURL底层统一进行调度和IO多路复用。
以下是极简的代码示例,展示了如何并行请求多个URL:
$urls = [
'http://www.ipipp.com/api1',
'http://www.ipipp.com/api2',
'http://www.ipipp.com/api3'
];
$mh = curl_multi_init();
$handles = [];
// 1. 初始化并加入批处理
foreach ($urls as $i => $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
// 2. 并行执行
$active = null;
do {
$status = curl_multi_exec($mh, $active);
// 等待活动连接减少IO消耗
if ($active) {
curl_multi_select($mh);
}
} while ($active && $status === CURLM_OK);
// 3. 获取结果并清理
$results = [];
foreach ($handles as $i => $ch) {
$results[$i] = curl_multi_getcontent($ch);
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
print_r($results);这种方式的优点是原生支持,性能较好;缺点是代码编写相对繁琐,且基于回调机制,如果业务逻辑复杂,代码可读性会下降。
二、GuzzleHTTP异步请求方案
Guzzle是目前PHP生态中最流行的HTTP客户端,它封装了底层的cURL Multi,提供了极其优雅的Promise接口,让异步代码看起来像同步代码一样直观。
在使用前,需通过Composer安装:composer require guzzlehttp/guzzle
require 'vendor/autoload.php';
use GuzzleHttpClient;
use GuzzleHttpPromise;
$client = new Client();
// 1. 创建异步请求Promise数组(不会立即执行)
$promises = [
'api1' => $client->getAsync('http://www.ipipp.com/api1'),
'api2' => $client->getAsync('http://www.ipipp.com/api2'),
'api3' => $client->getAsync('http://www.ipipp.com/api3'),
];
// 2. 并行执行并等待所有结果
$results = PromiseUtils::settle($promises)->wait();
// 3. 处理结果
foreach ($results as $key => $result) {
if ($result['state'] === 'fulfilled') {
echo $key . ' 请求成功: ' . $result['value']->getBody() . "n";
} else {
echo $key . ' 请求失败: ' . $result['reason']->getMessage() . "n";
}
}Guzzle的settle方法不仅让代码极其简洁,还能保证即使某个请求失败,也不会影响其他请求的执行,最终可以统一处理成功和失败的状态。这是日常开发中最推荐的方式。
三、Swoole协程方案
对于追求极致性能的场景,Swoole提供的协程(Coroutine)是最佳选择。Swoole在底层实现了协程调度,当遇到IO等待时,会自动挂起当前协程去执行其他协程,完全免去了手动编写异步回调的痛苦,实现了“同步的写法,异步的性能”。
// 开启一键协程化,让传统的同步阻塞函数变成异步IO
SwooleRuntime::enableCoroutine();
$results = [];
// 创建多个协程
go(function () use (&$results) {
$client = new SwooleCoroutineHttpClient('www.ipipp.com', 80);
$client->get('/api1');
$results['api1'] = $client->body;
$client->close();
});
go(function () use (&$results) {
$client = new SwooleCoroutineHttpClient('www.ipipp.com', 80);
$client->get('/api2');
$results['api2'] = $client->body;
$client->close();
});
// Swoole底层会自动并行调度上述协程使用Swoole协程,开发者无需关心底层的事件循环和回调嵌套,代码逻辑与传统的同步阻塞写法几乎一致,但运行效率却是并行非阻塞的,非常适合大规模爬虫或高并发微服务调用。
总结
原生cURL Multi:适合无第三方依赖限制的基础环境,代码较复杂,适合底层封装。
GuzzleHTTP:最推荐的日常开发方案,接口优雅,错误处理完善,适合绝大多数业务场景。
Swoole协程:极致性能方案,代码可读性最高,但需要服务器安装Swoole扩展,适合高并发常驻内存架构。
根据项目的实际依赖环境与性能需求,选择合适的并行处理方式,能够显著降低接口响应时间,提升系统的吞吐量。