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

PHP 微信支付 api.mch.weixin.qq.com 域名解析慢的 IPv6 根因與解決方案

     編輯:PHP 綜合知識

微信支付回調、統一下單等接口調用 api.mch.weixin.qq.com 時,若服務器啟用 IPv6,DNS 解析極慢(甚至超時),導致支付流程卡頓。本文深度分析根因,提供 cURL 強制 IPv4、系統級禁用 IPv6 等多層級解決方案。

現象複現

# wget 測試 
wget api.mch.weixin.qq.com 
# Resolving api.mch.weixin.qq.com... (長時間無響應) 

# 強制 IPv4 即秒開 
wget -4 api.mch.weixin.qq.com 
# Resolving api.mch.weixin.qq.com... 123.151.71.149, 123.151.79.109 
# Connecting... connected.

根因深度分析

  1. DNS 解析順序:現代 Linux getaddrinfo() 默認 AF_UNSPEC,優先查詢 AAAA (IPv6) 再查 A (IPv4)
  2. 微信域名 IPv6 配置缺陷:
    • api.mch.weixin.qq.com 無 AAAA 記錄
    • 其 CNAME 鏈指向 minorshort.weixin.qq.com,後者有 IPv6 CNAME 但無實際 AAAA 解析
    • 導致遞歸解析器在 IPv6 鏈上反復重試、超時、回退,耗時數秒
  3. 對比:qq.com 主域有完善 IPv6 部署,解析極快

代碼級驗證(C 調用 getaddrinfo)

#include  
#include  
#include  

int main() { 
    struct addrinfo hints = {}, *res; 
    hints.ai_family = AF_INET6; // 強制 IPv6 
    hints.ai_socktype = SOCK_STREAM; 
    
    int err = getaddrinfo("api.mch.weixin.qq.com", NULL, &hints, &res); 
    if (err) { 
        std::cerr << "getaddrinfo: " << gai_strerror(err) << std::endl; 
        return 1; 
    } 
    std::cout << "Success with IPv6\n"; 
    for (auto p = res; p; p = p->ai_next) { 
        char ip[INET6_ADDRSTRLEN]; 
        inet_ntop(AF_INET6, &((struct sockaddr_in6*)p->ai_addr)->sin6_addr, ip, sizeof(ip)); 
        std::cout << ip << std::endl; 
    } 
    freeaddrinfo(res); 
    return 0; 
}

編譯運行:g++ test.cpp -o test && ./test 將長時間無輸出,確認 IPv6 解析卡死。

解決方案(按推薦順序)

1. cURL 強制 IPv4(最推薦,零侵入)

$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, 'https://api.mch.weixin.qq.com/...'); 
curl_setopt($ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); // 關鍵行 
// 其他選項... 
$result = curl_exec($ch);

PHP cURL 7.10.8+ 支持 CURLOPT_IPRESOLVE,常量:CURL_IPRESOLVE_V4、CURL_IPRESOLVE_V6、CURL_IPRESOLVE_WHATEVER(默認)。

2. PHP 流上下文強制 IPv4

$ctx = stream_context_create([ 
    'http' => [ 
        'bindto' => '0.0.0.0:0', // 綁定 IPv4 任意地址 
    ], 
    'ssl' => [ 
        'verify_peer' => false, 
    ] 
]); 
$result = file_get_contents($url, false, $ctx);

3. 系統級禁用 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 生效

4. /etc/hosts 硬編碼(應急,不推薦長期)

123.151.71.149 api.mch.weixin.qq.com 
123.151.79.109 api.mch.weixin.qq.com
風險:微信更換機房 IP 變更將導致服務中斷,需監控並自動更新。

5. 調整 DNS 解析器配置

# /etc/resolv.conf 或 /etc/systemd/resolved.conf 
# 禁用 IPv6 查詢 
ResolveUnicastSingleLabel=no 
# 或使用只返回 A 記錄的 DNS(如 114.114.114.114)

語言無關的通用原則

語言/框架強制 IPv4 方式
JavaSystem.setProperty("java.net.preferIPv4Stack", "true")
GoDialer{Control: func(_, addr string) { return &net.TCPAddr{IP: net.ParseIP("0.0.0.0")} }}
Python requestsrequests.get(url, hooks={'response': lambda r: r.connection.sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)}) 或 HTTPAdapter 綁定 IPv4
Node.jshttp.request({family: 4, ...}) 或 dns.setDefaultResultOrder('ipv4first') (Node 17+)
Nginx upstreamresolver 114.114.114.114 ipv6=off;

建議微信團隊優化

  • 為 api.mch.weixin.qq.com 提供有效 AAAA 記錄,或移除 CNAME 鏈中的 IPv6 別名
  • 在開發文檔明確標註「建議服務端強制 IPv4 訪問」
  • 提供 HTTP/2 + IPv6 雙棧優化端點

在微信修復前,在所有支付相關 cURL 請求中添加 CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4 是成本最低、風險最小、效果最立竿見影的方案。

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