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

PHP 後端介面呼叫超時處理深度解析:cURL、Socket、Stream 實戰技巧

     編輯:PHP 綜合知識

前言

後端服務間呼叫若無超時保護,一個掛住的下游即可拖垮整個上游叢集。本文系統整理 cURL、Socket、Stream 三大呼叫方式的超時控制細節,並提供可直接落地的程式碼範本。

一、cURL 篇——最推薦的方案

核心超時參數

  • CURLOPT_TIMEOUT:整個請求上限秒數(重要)
  • CURLOPT_TIMEOUT_MS:毫秒級總超時(需 PHP 5.2.3+,cURL 7.16.2+)
  • CURLOPT_CONNECTTIMEOUT:連線階段上限秒數
  • CURLOPT_CONNECTTIMEOUT_MS:毫秒級連線超時
  • CURLOPT_DNS_CACHE_TIMEOUT:DNS 快取秒數,預設 120 秒

秒級超時標準範本

<?php
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 60,
    CURLOPT_HTTPHEADER     => $headers,
    CURLOPT_USERAGENT      => $_SERVER['HTTP_USER_AGENT'] ?? 'PHP/cURL',
]);
$data = curl_exec($ch);
?>

毫秒級超時完整範例(含錯誤處理)

<?php
$ch = curl_init('https://example.com/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_NOSIGNAL       => 1,           // 必須!避免訊號中斷
    CURLOPT_TIMEOUT_MS     => 200,         // 200 ms 總超時
]);
$data = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
curl_close($ch);

if ($errno) {
    error_log("cURL Error ($errno): $error");
} else {
    echo "Data received: $data";
}
?>

cURL 版本與 curl_multi 注意事項

  1. cURL ≥ 7.21.0 才可靠支援毫秒級超時。
  2. curl_multi 並行請求時,毫秒級超時可能不準確;單請求較穩定。

二、Stream / Socket 篇——原生不依賴擴充

手動計時法(適用於 Socket 精細控制)

<?php
$tmStart = gettimeofday();
// ... 執行 socket 讀寫 ...
$tmCurrent = gettimeofday();
$elapsedUs = ($tmCurrent['sec'] - $tmStart['sec']) * 1_000_000
           + ($tmCurrent['usec'] - $tmStart['usec']);
if ($elapsedUs > $readTimeoutUs) {
    return false; // 超時中斷
}
?>

內建 Stream 函式:stream_set_timeout + stream_get_meta_data

<?php
$timeout = 5; // 秒
$fp = fsockopen('example.com', 80, $errno, $errstr, $timeout);
if ($fp) {
    fwrite($fp, "GET / HTTP/1.0\r\nHost: example.com\r\nConnection: Close\r\n\r\n");
    stream_set_blocking($fp, true);  // 同步阻塞模式
    stream_set_timeout($fp, $timeout); // 設定讀寫超時

    $data = '';
    $info = stream_get_meta_data($fp);
    while (!feof($fp) && !$info['timed_out']) {
        $data .= fgets($fp, 4096);
        $info = stream_get_meta_data($fp); // 每輪更新 timed_out 旗標
    }

    if ($info['timed_out']) {
        echo "Connection Timed Out!";
    } else {
        echo $data;
    }
    fclose($fp);
}
?>

file_get_contents / fopen 統一超時(透過 Stream Context)

<?php
$ctx = stream_context_create([
    'http' => ['timeout' => 5] // 秒
]);
$text = file_get_contents('https://example.com/', 0, $ctx);
// 或
$fp = fopen('https://example.com/', 'r', false, $ctx);
?>

三、實戰經驗總結

場景推薦方案關鍵提醒
一般 HTTP API 呼叫cURL + CURLOPT_TIMEOUT務必設定連線與總超時分離
需毫秒級精確控制cURL + CURLOPT_TIMEOUT_MS + CURLOPT_NOSIGNAL確認 cURL ≥ 7.21.0
無法安裝 cURL 擴充Stream Context + stream_set_timeout需手動檢查 timed_out 旗標
長連線 / WebSocket / 自定協定Socket + 手動計時或 non-blocking + select()自行管理心跳與重連

結語

超時不是可有可無的選項,而是分散式系統生存的底線。請根據業務 SLA 選擇適當層級與精度,並在程式碼審查中強制檢查「每個外部呼叫都有超時設定」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved