HTTP 請求超時控制是網路程式健壯性的關鍵。PHP cURL 提供多層次超時參數,涵蓋連線階段、資料傳輸階段、DNS 快取等。本文系統整理各超時選項差異,提供秒級與毫秒級設定範例,並解釋 CURLOPT_NOSIGNAL 在毫秒級超時下的必要性。
| 常數 | 單位 | 控制階段 | 備註 |
|---|---|---|---|
CURLOPT_TIMEOUT | 秒 | 整個請求(連線+傳輸)最長執行時間 | 最常用,預設 0(不限制) |
CURLOPT_TIMEOUT_MS | 毫秒 | 整個請求最長執行毫秒數 | 需 cURL 7.16.2+、PHP 5.2.3+ |
CURLOPT_CONNECTTIMEOUT | 秒 | 連線階段等待時間(TCP 握手、SSL 協商) | 0 表示無限等待 |
CURLOPT_CONNECTTIMEOUT_MS | 毫秒 | 連線階段等待毫秒數 | 需 cURL 7.16.2+、PHP 5.2.3+ |
CURLOPT_DNS_CACHE_TIMEOUT | 秒 | DNS 快取存活時間 | 預設 120 秒,減少重複解析 |
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 60, // 整個請求最長 60 秒
CURLOPT_CONNECTTIMEOUT => 10, // 連線最長等待 10 秒
CURLOPT_HTTPHEADER => $headers,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'PHP-cURL',
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
// 錯誤碼 28 = CURLE_OPERATION_TIMEDOUT
throw new RuntimeException(curl_error($ch), curl_errno($ch));
}
curl_close($ch);
當使用 CURLOPT_TIMEOUT_MS 或 CURLOPT_CONNECTTIMEOUT_MS 時,**必須**啟用 CURLOPT_NOSIGNAL => 1,否則 cURL 會使用 Unix 訊號(SIGALRM)計時,導致多執行緒環境(如 PHP-FPM、Swoole)下訊號衝突、程式崩潰或超時不準。
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOSIGNAL => 1, // ★ 必須啟用,避免訊號衝突
CURLOPT_TIMEOUT_MS => 500, // 總超時 500 ms
CURLOPT_CONNECTTIMEOUT_MS=> 200, // 連線超時 200 ms
]);
$response = curl_exec($ch);
原理補充:cURL 傳統用 alarm() + 訊號處理器實現超時,但在多執行緒下訊號可能被錯誤執行緒捕獲。NOSIGNAL 改用輪詢機制,犧牲極微精度換取執行緒安全。
CURLOPT_DNS_CACHE_TIMEOUT => 60 或定期 curl_close() 釋放 handle 刷新快取。curl_errno() === 28 (CURLE_OPERATION_TIMEDOUT) 代表超時,28 含連線與傳輸兩階段,可結合 curl_getinfo($ch, CURLINFO_CONNECT_TIME) 判斷具體階段。timeout (float 秒) 與 connect_timeout 內部自動處理 NOSIGNAL 與單位換算,API 更安全。CURLOPT_TIMEOUT=30, CURLOPT_CONNECTTIMEOUT=10CURLOPT_NOSIGNAL=1, CURLOPT_TIMEOUT_MS=2000, CURLOPT_CONNECTTIMEOUT_MS=500CURLOPT_TIMEOUT=0 (不限總時長) + 應用層心跳控制精準的超時控制能有效防止資源耗盡、級聯失敗,是分散式系統穩定性的基石。