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

PHP file_get_contents 超時處理與 cURL 備援策略:跨國請求穩定性實戰

     編輯:PHP 綜合知識

問題現象

使用 file_get_contents 抓取遠端 API 時,若對方回應緩慢或斷線,PHP 會拋出 Fatal Error: Maximum execution time exceeded 或長時間阻塞導致後續程式無法執行。特別是跨國請求(如中國大陸訪問海外伺服器),stream_context_create(['http'=>['timeout'=>10]]) 往往失效。

方案一:Stream Context 設定超時(本機/內網有效)

<?php
$url = 'https://api.example.com/data';
$ctx = stream_context_create([
    'http' => [
        'timeout' => 10,           // 總超時(秒)
        'method'  => 'GET',
        'header'  => "User-Agent: PHP Bot\r\n",
    ]
]);
$result = @file_get_contents($url, 0, $ctx);

if ($result !== false) {
    var_dump($result);
} else {
    echo "請求失敗或超時";
}
?>

實測結論: 同機房/本機測試有效;跨國/高延遲環境下 timeout 參數常被忽略,請求仍會阻塞至 PHP max_execution_time。

方案二:cURL 完整超時控制(推薦生產環境)

<?php
function zhoz_get_contents($url, $connectTimeout = 5, $totalTimeout = 10) {
    $ch = curl_init();
    curl_setopt_array($ch, [
        CURLOPT_URL            => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER         => false,
        CURLOPT_CONNECTTIMEOUT => $connectTimeout, // 連線建立超時
        CURLOPT_TIMEOUT        => $totalTimeout,   // 整體執行超時
        CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; PHP Bot)',
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 3,
        CURLOPT_SSL_VERIFYPEER => false, // 僅測試
    ]);
    
    $content = curl_exec($ch);
    $errno   = curl_errno($ch);
    $error   = curl_error($ch);
    curl_close($ch);
    
    if ($errno) {
        // 可拋出異常或記錄日誌
        error_log("cURL [$errno] $error for $url");
        return false;
    }
    return $content;
}

// 使用範例
try {
    echo date('Y-m-d H:i:s') . "\n";
    $buffer = zhoz_get_contents('https://slow-api.example.com/data');
    echo date('Y-m-d H:i:s') . "\n";
    
    if (empty($buffer)) {
        echo "回應為空\n";
    } else {
        echo "成功取得資料 (" . strlen($buffer) . " bytes)\n";
    }
} catch (Exception $e) {
    echo "異常: " . $e->getMessage() . "\n";
}
?>

方案三:智慧雙模式備援函式(自動偵測最佳方案)

<?php
function vita_get_url_content($url) {
    // 1. 優先 cURL(功能完整、超時精確、支援 HTTPS/Proxy)
    if (function_exists('curl_init')) {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL            => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_TIMEOUT        => 10,
            CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; PHP Bot)',
            CURLOPT_SSL_VERIFYPEER => false,
        ]);
        $content = curl_exec($ch);
        curl_close($ch);
        return $content;
    }
    
    // 2. 次之 file_get_contents + Context(需 allow_url_fopen=On)
    if (function_exists('file_get_contents') && ini_get('allow_url_fopen')) {
        $ctx = stream_context_create(['http' => ['timeout' => 10]]);
        return @file_get_contents($url, 0, $ctx);
    }
    
    // 3. 理論上不會到這裡,保留 fsockopen 兜底
    trigger_error('No HTTP client available (cURL/fopen)', E_USER_WARNING);
    return false;
}
?>

生產環境檢查清單

  1. ☑ 伺服器安裝 php-curl 並啟用 extension=curl
  2. ☑ 設定 CURLOPT_CONNECTTIMEOUT(連線階段)與 CURLOPT_TIMEOUT(總執行)分離
  3. ☑ 啟用 CURLOPT_SSL_VERIFYPEER 並配置 CURLOPT_CAINFO 指向 cacert.pem
  4. ☑ 設定合理 User-Agent,避免被防火牆攔截
  5. ☑ 使用 curl_errno()/curl_error() 進行錯誤處理,避免靜默失敗
  6. ☑ 高併發場景改用 curl_multi 非同步批次請求
  7. ☑ 關鍵 API 加入重試機制(指數退避 + 亂數抖動)

進階:非同步併發請求範例

<?php
function multiRequest($urls, $options = []) {
    $handles = [];
    $mh = curl_multi_init();
    
    foreach ($urls as $i => $url) {
        $ch = curl_init($url);
        curl_setopt_array($ch, array_merge([
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_TIMEOUT        => 10,
        ], $options));
        curl_multi_add_handle($mh, $ch);
        $handles[$i] = $ch;
    }
    
    $running = null;
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh);
    } while ($running > 0);
    
    $results = [];
    foreach ($handles as $i => $ch) {
        $results[$i] = [
            'content'  => curl_multi_getcontent($ch),
            'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
            'error'    => curl_error($ch),
        ];
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);
    return $results;
}
?>
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved