多URL状态检测:curl_multi执行过慢问题及代码优化求助
优化curl_multi批量域名检测的速度问题
嘿,我来帮你捋捋为啥你的curl_multi批量检测域名速度慢,再给你几个实打实的优化方案。先把你贴的代码补全展示下(看起来你没写完,但核心逻辑已经能看出来):
class BotCronJobs extends Controller { public function __construct() { } public function index() { $Query = servers::all(); $urls = []; foreach ($Query as $item){ $urls[$item->id] = $item->serverUrl; } var_dump($this->test($urls)); } public function test($urls = []) { $status = []; $mh = curl_multi_init(); foreach($urls as $key => $value){ $ch[$key] = curl_init($value); // 常规的基础配置(你没写完的部分) curl_setopt($ch[$key], CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($mh, $ch[$key]); } // 常见的执行循环(这部分往往是效率瓶颈) $running = null; do { curl_multi_exec($mh, $running); } while ($running > 0); // 收集结果并清理资源 foreach($urls as $key => $value){ $status[$key] = curl_getinfo($ch[$key], CURLINFO_HTTP_CODE); curl_multi_remove_handle($mh, $ch[$key]); curl_close($ch[$key]); } curl_multi_close($mh); return $status; } }
核心优化方向
1. 砍掉curl请求的冗余操作
很多时候慢不是curl_multi的锅,而是每个请求做了太多无用功:
- 设置
CURLOPT_NOBODY => true:只获取HTTP头信息,不用下载响应体,直接省掉带宽和响应解析的时间 - 严格设置超时:
CURLOPT_CONNECTTIMEOUT => 3(连接超时3秒)、CURLOPT_TIMEOUT => 5(总请求超时5秒),避免单个慢请求拖垮整个批次 - 禁用不需要的功能:比如
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1(非必要不用HTTP2),测试/内部场景下可以关掉SSL验证(CURLOPT_SSL_VERIFYPEER => false、CURLOPT_SSL_VERIFYHOST => false)
2. 优化curl_multi的执行循环
你原来的do-while是空轮询,会疯狂占用CPU资源,导致整体效率下降。换成curl_multi_select等待活动连接,减少CPU空转:
$running = null; do { // 等待1秒,有活动连接就继续执行 $fd = curl_multi_select($mh, 1); if ($fd === -1) { usleep(1000); // 极端场景下避免CPU跑满 continue; } curl_multi_exec($mh, $running); } while ($running > 0);
3. 控制并发数,避免资源耗尽
一下子扔几百上千个请求,服务器的文件句柄、网络连接都会被占满,反而变慢。建议把并发数控制在50-100之间(根据服务器配置调整):
$maxConcurrent = 50; $activeHandles = []; $mh = curl_multi_init(); foreach ($urls as $key => $url) { $ch = curl_init($url); // 套上前面说的优化配置 curl_setopt($ch, CURLOPT_NOBODY, true); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($mh, $ch); $activeHandles[$key] = $ch; // 达到最大并发时先处理一批 if (count($activeHandles) >= $maxConcurrent) { do { $fd = curl_multi_select($mh, 1); if ($fd === -1) { usleep(1000); continue; } curl_multi_exec($mh, $running); } while ($running > 0); // 清理已完成的请求 foreach ($activeHandles as $k => $handle) { $status[$k] = curl_getinfo($handle, CURLINFO_HTTP_CODE); curl_multi_remove_handle($mh, $handle); curl_close($handle); } $activeHandles = []; } } // 处理剩余的请求 do { $fd = curl_multi_select($mh, 1); if ($fd === -1) { usleep(1000); continue; } curl_multi_exec($mh, $running); } while ($running > 0); foreach ($activeHandles as $k => $handle) { $status[$k] = curl_getinfo($handle, CURLINFO_HTTP_CODE); curl_multi_remove_handle($mh, $handle); curl_close($handle); } curl_multi_close($mh);
4. 复用连接池,减少TCP握手开销
启用TCP连接复用,相同域名的请求可以复用已建立的连接,省掉三次握手的时间:
// 初始化连接共享池 $share = curl_share_init(); curl_share_setopt($share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); // 给每个curl句柄绑定共享池 curl_setopt($ch, CURLOPT_SHARE, $share);
5. 其他小细节优化
- 提前过滤无效URL:去掉空值、格式错误的URL,避免无效请求浪费资源
- 只获取需要的信息:用
curl_getinfo只取CURLINFO_HTTP_CODE,不要获取整个info数组 - 延迟非必要操作:比如数据库写入、日志记录,放到所有请求完成后统一处理,不要在循环里穿插
把这些优化点整合进去,你的批量域名检测速度应该能提升好几倍——核心就是减少单个请求的开销、优化curl_multi的执行逻辑、合理控制并发,以及复用连接资源。
内容的提问来源于stack exchange,提问作者M.D
相关产品推荐
相关产品推荐

