串行請求總耗時 = Σ(單次請求耗時)。對多個獨立 API 呼叫、爬蟲抓取、微服務聚合,併發可將總耗時降至 ≈ max(單次請求耗時),效能提升顯著。
<?php
class Http_MultiRequest {
private $urls = [];
private $options = [];
public function __construct($options = []) {
$this->setOptions($options);
}
public function setUrls(array $urls) {
$this->urls = $urls;
return $this;
}
public function setOptions(array $options) {
// 預設選項
$defaults = [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP Http_MultiRequest)',
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_HEADER => 0,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => false, // 測試環境;生產請設 true 並配置 CA
];
// POST 資料處理
if (isset($options['HTTP_POST'])) {
$defaults[CURLOPT_POST] = 1;
$defaults[CURLOPT_POSTFIELDS] = $options['HTTP_POST'];
unset($options['HTTP_POST']);
}
$this->options = array_merge($defaults, $options);
}
/** 併發執行所有 URL */
public function exec() {
if (empty($this->urls) || !is_array($this->urls)) return false;
$mh = curl_multi_init();
$handles = [];
// 建立並加入 handles
foreach ($this->urls as $k => $url) {
$ch = curl_init($url);
curl_setopt_array($ch, $this->options);
curl_multi_add_handle($mh, $ch);
$handles[$k] = $ch;
}
// 執行併發 —— 改良版:使用 curl_multi_select 避免 CPU 100%
$this->execMultiHandle($mh);
// 收集結果
$results = [];
foreach ($handles as $k => $ch) {
$results[$k] = curl_multi_getcontent($ch);
curl_multi_remove_handle($mh, $ch);
}
curl_multi_close($mh);
return $results;
}
/** 單一請求(串行) */
public function execOne($url) {
if (empty($url)) return false;
$ch = curl_init($url);
curl_setopt_array($ch, $this->options);
$content = curl_exec($ch);
curl_close($ch);
return $content;
}
/** 改良版併發迴圈:避免忙等待消耗 CPU */
private function execMultiHandle($mh) {
$running = null;
do {
// 執行可用的傳輸
curl_multi_exec($mh, $running);
// 等待活動,最長 1 秒 —— 關鍵優化!
if ($running > 0) {
curl_multi_select($mh, 1.0);
}
} while ($running > 0);
}
}
?>
<?php
$urls = [
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
"https://httpbin.org/delay/1",
];
$client = new Http_MultiRequest();
// 併發測試
$start = microtime(true);
$parallelData = $client->setUrls($urls)->exec();
$parallelTime = microtime(true) - $start;
echo "併發耗時: {$parallelTime} 秒\n"; // 約 1 秒
// 串行測試
$start = microtime(true);
$serialData = [];
foreach ($urls as $url) {
$serialData[] = $client->execOne($url);
}
$serialTime = microtime(true) - $start;
echo "串行耗時: {$serialTime} 秒\n"; // 約 5 秒
echo "加速比: " . round($serialTime / $parallelTime, 1) . "x\n";
?>
do { curl_multi_exec($mh, $running); } while ($running > 0);curl_multi_select($mh, 1.0) 讓進程睡眠等待 I/O 事件
curl_errno()、curl_getinfo($ch, CURLINFO_HTTP_CODE)CURLOPT_TIMEOUT、CURLOPT_CONNECTTIMEOUT| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| cURL Multi | 無額外依賴、支援 HTTPS、成熟穩定 | 仍是阻塞式、單進程併發上限受限 | 中小規模 API 聚合、爬蟲 |
| Swoole/Rocket | 真非阻塞、萬級併發、協程寫法 | 需安裝擴充、學習曲線陡峭 | 高併發微服務、即時通訊 |
| Guzzle Promise | PSR-7 相容、Middleware 機制豐富 | 底層仍用 cURL Multi | 現代 PHP 專案、框架整合 |