2024/10/28

透過 curl 的 timeout 設定識別 http 的網路連線壅塞問題

網路連線壅塞是一個難解的問題, 因為網路會產生抖動(Jitter)現象, 抖動發生時, 連線會突然變得很慢. 當狀況改善後就會回復. 但很大的原因是因為是因為 DNS 解析或是 TCP 交握的過程產生的問題. 

curl 連線到一個 HTTP 網址時,其工作流程包括以下幾個主要步驟:

1. DNS 查詢

  • 目標:解析主機名 (如 example.com) 對應的 IP 位址。
  • 過程curl 通過 DNS 伺服器進行查詢,獲取目標伺服器的 IP 地址。
  • 結果:若查詢成功,返回 IP 地址,curl 將繼續下一步。若查詢失敗,curl 則返回 DNS 錯誤並中止。

2. TCP 三向交握 (Three-Way Handshake)

  • 目標:建立與目標伺服器的 TCP 連線。
  • 過程curl 通過系統內核發送一個 SYN 封包,目標伺服器回應 SYN-ACK,然後 curl 返回 ACK 完成三向交握,建立起 TCP 連線。
  • 結果:若在 --connect-timeout 設定時間內未完成三向交握,則連線失敗並返回超時錯誤。

3. 發送 HTTP 請求

  • 目標:向伺服器發送具體的 HTTP 請求,根據 URL 設定不同的請求方法(如 GETPOST)。
  • 過程curl 構建 HTTP 請求標頭並附加任何所需的數據(如表單數據),然後通過已建立的 TCP 連線將請求發送到伺服器。
  • 結果:伺服器接收請求並準備回應,若過程中出現網路問題,則請求可能中止或失敗。

4. 伺服器處理請求並返回回應

  • 目標:伺服器根據請求的 URL 路徑處理並生成對應的回應內容。
  • 過程:伺服器確認請求內容後,由 HTTP 伺服器(如 httpd)根據需求(例如讀取靜態文件或調用後端服務)生成回應,並加上適當的 HTTP 狀態碼和標頭。
  • 結果:伺服器將回應內容傳回給 curl 客戶端。

5. 接收 HTTP 回應

  • 目標curl 從伺服器接收回應數據,並在終端或指定的輸出目標中顯示。
  • 過程curl 讀取 HTTP 回應標頭(包括狀態碼,如 200 OK404 Not Found 等)及內容,並根據需要顯示、保存或處理該回應。
  • 結果:若指定了輸出文件,curl 將回應寫入文件;若未指定,則在終端中顯示。若在接收期間出現中斷或錯誤,則可能返回部分內容或失敗。

6. TCP 連線關閉 (四次揮手)

  • 目標:完成數據傳輸後,curl 與伺服器結束連線。
  • 過程curl 發送 FIN 封包,伺服器回應 ACK,並發送自己的 FIN 封包,最後 curl 回應 ACK,四次揮手完成。
  • 結果:TCP 連線釋放,curl 任務完成。

curl 的 HTTP 工作流程可概括為:DNS 查詢 -> TCP 連線建立 -> 發送 HTTP 請求 -> 伺服器處理並回應 -> 接收回應 -> TCP 連線關閉。


在 HTTP 壅塞情況下 curl 參數,--connect-timeout--max-time 的表現會有所不同,具體取決於壅塞發生的階段:

  1. --connect-timeout

    • --connect-timeout 主要針對連線的建立過程(TCP 三次握手)。如果壅塞導致連線過程延遲,例如因為服務器回應延遲或網路繁忙而無法快速建立連線,這個參數會限制等待的時間。
    • 在連線成功建立後,--connect-timeout 就不再生效,因此這個參數並不會影響數據傳輸階段的壅塞情況。
    • 情境:假如壅塞發生在連線建立之前,curl 會在達到 --connect-timeout 設定的秒數後中止嘗試。
  2. --max-time

    • --max-time 影響整個請求的持續時間,因此即使成功建立連線,但在傳輸數據時因壅塞而變慢,--max-time 仍會在達到設定的上限時間後中止請求。
    • 這個參數可以在連線建立和數據傳輸的任一階段發揮作用,是更全面的時間限制。
    • 情境:如果壅塞發生在連線建立之後,curl 在達到 --max-time 設定的秒數後會中止整個請求。



使用 --connect-timeout--max-time 選項來測試壅塞情境。

  • bash 指令
    curl --connect-timeout 5 --max-time 10 http://example.com/slow
  • 說明--connect-timeout 5 設置了連線超時為 5 秒,--max-time 10 設置整個請求的最大等待時間為 10 秒。

  • 效果:如果伺服器在 5 秒內無法建立連線,或者請求超過 10 秒未完成,curl 會中止並返回超時錯誤。


在 PHP 中,我們可以利用 CURLOPT_CONNECTTIMEOUTCURLOPT_TIMEOUT 這兩個 cURL 選項來控制 HTTP 請求的超時行為,以便提早識別出 HTTP 壅塞的情況。這兩個參數的作用和 curl 指令行工具中的 --connect-timeout--max-time 類似:


$ch = curl_init();

// 設置要請求的 URL

curl_setopt($ch, CURLOPT_URL, "http://example.com");

// 設置連線超時為 5 秒,判斷連線建立過程中是否有壅塞

curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);

// 設置整個請求超時為 10 秒,應對數據傳輸階段的壅塞

curl_setopt($ch, CURLOPT_TIMEOUT, 10);

// 設置返回結果而不是直接輸出

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// 執行請求並取得回應

$response = curl_exec($ch);

// 檢查是否有錯誤

if (curl_errno($ch)) {

    echo "Error: " . curl_error($ch);

} else {

    echo "Response: " . $response;

}

// 關閉 cURL 資源

curl_close($ch);