PHP 批量异步请求的方法
刚子 发布于 阅读:119
极简最优:单进程 + Swoole\Coroutine\Http\Client(协程批量)
<?php
// 需安装 Swoole 扩展(pecl install swoole)
Co\run(function () {
$urls = []; // 你的 1000 条 URL
for ($i = 0; $i < 1000; $i++) {
$urls[] = "https://www.xx.com/test.php?order_no=$i";
}
foreach ($urls as $url) {
go(function () use ($url) {
$cli = new Swoole\Coroutine\Http\Client(parse_url($url, PHP_URL_HOST), 443, true);
$cli->set([
'timeout' => 0.05, // 50 ms 超时,发完即走
'ssl_verify_peer' => false,
]);
$cli->get(parse_url($url, PHP_URL_PATH) . '?' . parse_url($url, PHP_URL_QUERY));
$cli->close(); // 立即关闭,不等待响应
});
}
});
echo "1000 条已发出\n";
使用CURL
<?php
// fire.php
$urls = [];
$total = 20000; // 总请求数
$concur = 50; // 并发数,建议10~50(根据服务器配置调整)
$delay = 50000; // 批次间隔(微秒),50ms
// 生成URL列表
for ($i = 0; $i < $total; $i++) {
$urls[] = 'https://www.ss.com/test.php?order_no=' . $i;
}
// 过滤无效URL(避免无效请求消耗资源)
$urls = array_filter($urls, function($url) {
return filter_var($url, FILTER_VALIDATE_URL) !== false;
});
if (empty($urls)) {
die("无有效URL可请求\n");
}
$chunks = array_chunk($urls, $concur);
$batchCount = count($chunks);
$currentBatch = 1;
foreach ($chunks as $batch) {
// 1. 转义所有URL(防止命令注入,处理特殊字符)
$escapedUrls = array_map('escapeshellarg', $batch);
// 2. 构建单条curl命令,用--parallel并行处理当前批次(减少进程创建开销)
$urlsStr = implode(' ', $escapedUrls);
$cmd = "curl -s -m 1 -k --parallel --parallel-max {$concur} {$urlsStr} > /dev/null 2>&1 &";
// 3. 执行命令(单批次仅1次系统调用)
shell_exec($cmd);
// 4. 输出进度(可选,便于监控)
echo "已发送第{$currentBatch}/{$batchCount}批(共".count($batch)."条)\n";
$currentBatch++;
// 5. 批次间隔(最后一批无需等待)
if ($currentBatch <= $batchCount) {
usleep($delay);
}
}
echo "共".count($urls)."条有效URL已分批异步发出\n";
?>
第三种方法
<?php
$total = 20000;
$concur = 50; // 同时并发的句柄数(可调 20~100)
$mh = curl_multi_init();
$chs = [];
// 1. 生成干净 URL
for ($i = 0; $i < $total; $i++) {
$url = "https://www.x.cn/test.php?order_no=$i";
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => false, // 不取内容
CURLOPT_TIMEOUT => 1, // 1 秒超时
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_NOSIGNAL => true, // 防止超时信号
]);
curl_multi_add_handle($mh, $ch);
$chs[] = $ch;
// 每 $concur 条批量执行一次
if (count($chs) >= $concur) {
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.001); // 非阻塞事件循环
} while ($running);
// 清理这批句柄
foreach ($chs as $ch) {
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
$chs = [];
}
}
// 2. 处理剩余不足 $concur 条
if ($chs) {
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh, 0.001);
} while ($running);
foreach ($chs as $ch) {
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
}
curl_multi_close($mh);
echo "done\n";