在 PHP 中,我们经常需要与外部 API、网站或服务进行网络请求。常规的 curl_exec() 单个请求虽然简单,但在高并发场景下,它的性能瓶颈非常明显:每个请求都是顺序执行的,等待上一个请求完成才能开始下一个,导致整体耗时线性增长。
为了提高网络请求效率,PHP 提供了两个非常强大的工具:curl_share_init() 和 curl_multi_* 系列函数。本文将深入介绍如何使用它们实现高性能并发请求。
curl_share_init() 用于在多个 cURL 句柄之间共享数据,比如 DNS 缓存、Cookie、SSL 会话等。
举个例子:如果你需要同时请求多个相同域名的地址,启用共享 DNS 缓存可以显著减少 DNS 查询时间。
$sh = curl_share_init();
curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
这段代码初始化了一个共享句柄,并设置为共享 DNS 数据。接下来我们会将这个共享句柄绑定到每个 curl 请求上。
curl_multi_* 系列函数允许我们同时发起多个 HTTP 请求,而不是像 curl_exec() 那样一个接一个地发。
主要函数包括:
curl_multi_init(): 初始化一个 multi 句柄。
curl_multi_add_handle(): 将单个 curl 句柄加入 multi 句柄。
curl_multi_exec(): 运行 multi 句柄中所有的请求。
curl_multi_select(): 阻塞等待活动的连接。
curl_multi_getcontent(): 获取每个请求的返回内容。
curl_multi_remove_handle(): 完成后移除单个句柄。
curl_multi_close(): 关闭 multi 句柄。
下面是一个完整示例,展示如何使用 curl_share_init() 和 curl_multi_* 系列函数并发请求多个地址。
<?php
// 要請求的 URL 列表
$urls = [
'https://m66.net/api/endpoint1',
'https://m66.net/api/endpoint2',
'https://m66.net/api/endpoint3'
];
// 初始化共享句柄
$sh = curl_share_init();
curl_share_setopt($sh, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
// 初始化 multi 句柄
$mh = curl_multi_init();
$curl_handles = [];
// 為每個 URL 創建單獨的 curl 句柄
foreach ($urls as $i => $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SHARE, $sh); // 綁定共享句柄
curl_multi_add_handle($mh, $ch);
$curl_handles[$i] = $ch;
}
// 執行所有請求
$running = null;
do {
$status = curl_multi_exec($mh, $running);
if ($status > 0) {
echo "cURL error: " . curl_multi_strerror($status) . "\n";
}
// 等待活動連接
curl_multi_select($mh);
} while ($running > 0);
// 獲取結果
$responses = [];
foreach ($curl_handles as $i => $ch) {
$responses[$i] = curl_multi_getcontent($ch);
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
// 關閉 multi 和 share 句柄
curl_multi_close($mh);
curl_share_close($sh);
// 輸出結果
foreach ($responses as $i => $response) {
echo "Response from URL $i:\n$response\n\n";
}
?>
使用 curl_multi_*,所有请求会尽量并发执行,整体耗时接近最长单个请求的耗时,而不是总和。
使用 curl_share_init(),则能减少重复的 DNS 查询、SSL 握手等开销,进一步优化性能。
在高并发、需要频繁请求相同域名 API 的场景(例如同时抓取多个接口、批量爬取网页、同步多个微服务数据等),这种组合能显著提升效率。
DNS 共享不是总能显著提速:主要在相同域名、多请求下见效。
合理设置超时:并发中某个请求卡住可能影响整体。
控制并发数量:请求太多可能导致服务器或本地资源耗尽,可以用信号量或批次分发。
错误处理:示例中未详细处理单个请求失败,实际应用需要补充。