首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 多線程併發請求實作:cURL Multi Handle 並行抓取完整封裝類別

     編輯:PHP 相關知識

PHP cURL 多線程併發請求:Http_MultiRequest 類別完整實作

為什麼需要併發請求?

串行請求總耗時 = Σ(單次請求耗時)。對多個獨立 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";
?>

關鍵優化細節:curl_multi_select

原始碼常見錯誤:do { curl_multi_exec($mh, $running); } while ($running > 0);
缺陷:無阻塞等待 → CPU 100% 狂轉(忙等待)
修正:加入 curl_multi_select($mh, 1.0) 讓進程睡眠等待 I/O 事件

進階擴充建議

  • 錯誤處理:檢查 curl_errno()、curl_getinfo($ch, CURLINFO_HTTP_CODE)
  • 超時控制:CURLOPT_TIMEOUT、CURLOPT_CONNECTTIMEOUT
  • 限流機制:分批次處理大量 URL,避免連線耗盡
  • 重試邏輯:指數退避重試失敗請求
  • 非阻塞框架:Swoole、ReactPHP、Amp 實現真非阻塞 I/O

適用場景對比

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
方案優點缺點適用場景
cURL Multi無額外依賴、支援 HTTPS、成熟穩定仍是阻塞式、單進程併發上限受限中小規模 API 聚合、爬蟲
Swoole/Rocket真非阻塞、萬級併發、協程寫法需安裝擴充、學習曲線陡峭高併發微服務、即時通訊
Guzzle PromisePSR-7 相容、Middleware 機制豐富底層仍用 cURL Multi現代 PHP 專案、框架整合