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

PHP cURL/wget 執行緩慢根因:IPv6 DNS 解析超時導致 5 秒延遲

     編輯:PHP 相關知識

問題現象

  • curl_exec() 執行需等待 ~5 秒才回應
  • 同樣 URL 瀏覽器秒開
  • wget 命令列卡在 Resolving domain... 階段
  • ping domain 卻正常秒回

根本原因分析

系統啟用 IPv6,但目標域名無 AAAA 記錄(IPv6 地址),導致:

  1. cURL/wget 預設優先嘗試 IPv6 解析(AAAA 記錄)
  2. DNS 伺服器查無 AAAA 記錄,需等待超時(通常 5 秒)
  3. 超時後回退 IPv4 (A 記錄) 解析,才真正連線
  4. ping 可能僅查 A 記錄,或本地 /etc/hosts 有快取,故不受影響

驗證方法

# 強制 IPv4 測試
wget -4 example.com          # 瞬間完成
curl --ipv4 example.com      # 瞬間完成

# 強制 IPv6 測試(應超時或失敗)
wget -6 example.com          # 等待超時
curl --ipv6 example.com      # 等待超時

# 查看 DNS 記錄
dig example.com AAAA         # 無回應或 NXDOMAIN
dig example.com A            # 有回應

解決方案

方案一:cURL 選項強制 IPv4(推薦、零風險)

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/data');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

// 關鍵:強制解析為 IPv4
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);

$response = curl_exec($ch);
curl_close($ch);

方案二:系統層面停用 IPv6(影響面大,謹慎)

# 暫時停用(重啟失效)
sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1

# 永久停用 /etc/sysctl.conf
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
net.ipv6.conf.lo.disable_ipv6 = 1
# sysctl -p 生效

方案三:/etc/hosts 硬編碼 IPv4(臨時繞過)

# /etc/hosts
192.168.1.100  api.example.com

方案四:DNS 伺服器配置(根治)

  • 為域名新增 AAAA 記錄(指向 ::1 或真實 IPv6)
  • 或在 DNS 伺服器設定 option timeout:1 縮短超時

PHP 程式碼最佳實踐封裝

class HttpClient {
    private $defaultOptions = [
        CURLOPT_TIMEOUT => 10,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,  // 預設強制 IPv4
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_USERAGENT => 'MyApp/1.0 (PHP/' . PHP_VERSION . ')',
    ];

    public function get(string $url, array $options = []): string {
        $ch = curl_init($url);
        curl_setopt_array($ch, $this->defaultOptions + $options);
        $response = curl_exec($ch);
        
        if ($response === false) {
            $error = curl_error($ch);
            $errno = curl_errno($ch);
            curl_close($ch);
            throw new \RuntimeException("cURL 錯誤 [$errno]: $error");
        }
        
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        if ($httpCode >= 400) {
            throw new \RuntimeException("HTTP $httpCode: $response");
        }
        
        return $response;
    }
    
    // 允許特定請求使用 IPv6
    public function getIpv6(string $url): string {
        return $this->get($url, [CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V6]);
    }
}

延伸:Guzzle 7 設定

use GuzzleHttp\Client;

$client = new Client([
    'curl' => [
        CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4,
    ],
    'timeout' => 10,
    'connect_timeout' => 5,
]);

$response = $client->request('GET', 'https://api.example.com/data');

排查清單

現象可能原因驗證命令解決
cURL/wget 慢、ping 快IPv6 AAAA 記錄缺失導致超時回退dig domain AAAA / wget -4 domainCURLOPT_IPRESOLVE=V4
間歇性慢DNS 伺服器不穩/EDNS 問題dig @8.8.8.8 domain換 DNS/縮短 timeout
特定域名慢權威 DNS 無 IPv6 回應dig +trace domain聯繫域名方新增 AAAA
容器內慢Docker 網路 IPv6 設定docker run --dns=8.8.8.8指定 DNS/停用 IPv6

結語

**「網路慢先查 DNS,DNS 慢先查 IPv6」** 是排查 cURL/wget 緩慢的黃金法則。現代 Linux 發行版預設啟用 IPv6,但許多內部/舊域名缺乏 AAAA 記錄,導致 5 秒預設超時成為隱形效能殺手。在 HTTP 客戶端預設強制 IPv4 (CURL_IPRESOLVE_V4) 是最低成本、最高相容的緩解方案,長期應推動基礎設施完善 IPv6 部署。

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