如何将单线程cURL改造为curl_multi_init多线程并发请求
基于curl_multi_init的并发cURL请求实现
原有改造代码存在4个核心问题:
- 仅初始化1个cURL句柄,循环添加到multi句柄时反复复用同一资源,无法生成多个独立并发请求
- POST参数传值错误,传入了整个分块数组而非单条请求对应的参数
- 请求执行完成后仅读取了1个句柄的返回内容,未遍历所有并发请求获取全部结果
- 未对每个独立cURL句柄做单独的初始化、移除、关闭操作,会造成资源泄漏
另外提供的单线程示例代码本身存在变量未定义问题:循环中$res[$key]的$key从未赋值,会产生运行警告。
正确实现代码
下面是分块并发(默认每批10个请求)的可运行版本,完全兼容原有POST请求逻辑,返回结果结构和单线程版本对齐:
<?php // 目标请求地址 $url = 'http://localhost/test.php'; $res = []; // 生成100条待请求的参数,对应原示例100次请求的场景 $requestList = []; for ($i=0; $i < 100; $i++) { $requestList[] = 'key='.$i; } // 按每批10个请求分块,避免并发数过高被目标站限流 $chunkList = array_chunk($requestList, 10, true); // 遍历分块逐批执行并发请求 foreach ($chunkList as $chunkIndex => $chunkParams) { $multiHandle = curl_multi_init(); $childHandles = []; // 为当前批次的每个请求初始化独立cURL句柄 foreach ($chunkParams as $paramIndex => $postData) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-type:application/x-www-form-urlencoded;charset=utf-8;"]); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); // 把句柄加入multi实例,同时存到数组方便后续取结果 curl_multi_add_handle($multiHandle, $ch); $childHandles[$paramIndex] = $ch; } // 标准并发请求轮询逻辑 $active = null; do { $statusCode = curl_multi_exec($multiHandle, $active); } while ($statusCode == CURLM_CALL_MULTI_PERFORM); while ($active && $statusCode == CURLM_OK) { if (curl_multi_select($multiHandle) != -1) { do { $statusCode = curl_multi_exec($multiHandle, $active); } while ($statusCode == CURLM_CALL_MULTI_PERFORM); } } // 逐个获取当前批次所有请求的返回结果 foreach ($childHandles as $paramIndex => $ch) { // 计算全局结果索引,和原单线程返回结构保持一致 $globalIndex = $chunkIndex * 10 + $paramIndex; $res[$globalIndex] = curl_multi_getcontent($ch); // 移除并关闭单个句柄,避免资源泄漏 curl_multi_remove_handle($multiHandle, $ch); curl_close($ch); } // 关闭当前批次的multi句柄 curl_multi_close($multiHandle); } var_dump($res);
使用说明
- 每批并发数建议控制在5-20区间,具体看目标服务的承载能力,并发过高容易被限流、触发防火墙拦截,甚至造成本地端口耗尽
- 如果需要自定义请求头、Cookie、代理、超时时间等配置,在初始化单个
$ch的时候加对应的curl_setopt即可,写法和单线程cURL完全一致 - 如果不需要获取请求返回内容,可以把
CURLOPT_RETURNTRANSFER设为0,大幅降低内存占用
内容的提问来源于stack exchange,提问作者Wei Zhao
相关产品推荐
相关产品推荐

