2014年1月22日 星期三

PHP curl 在 linux 設定 timeout 小於 1 秒,執行結果總是 timeout 的問題

PHP curl 的 CURLOPT_TIMEOUT_MS 選項,可以設定 curl 最多執行幾毫秒就執行失敗 timeout,在 windows 上面可以正常運作。

但在 linux 上,若設定 timeout 小於 1 秒,而且系統是使用 standard system name resolver(系統標準的DNS解析) 時,須同時將 CURLOPT_NOSIGNAL 設為 1,才可以正常運作,否則不管執行時間是多少,都會得到執行逾時的錯誤訊息。(但缺點是, DNS 解析將不受逾時限制)


官網 CURLOPT_CONNECTTIMEOUT_MS 選項的說明
http://www.php.net/manual/en/function.curl-setopt.php
The number of milliseconds to wait while trying to connect. Use 0 to wait indefinitely. If libcurl is built to use the standard system name resolver, that portion of the connect will still use full-second resolution for timeouts with a minimum timeout allowed of one second.


原因可以參考 风雪之隅:Curl的毫秒超时的一个”Bug”,有詳細的說明。

在 PHP 官網也有這個問題的相關討論
http://www.php.net/manual/en/function.curl-setopt.php#104597

以下用官網上面的範例,稍微修改,做個測試。

測試 1:
將 CURLOPT_NOSIGNAL 設為 0,或沒設定,curl 執行結果總是 timeout。
測試網頁:http://demo.cinc.biz/curl_test_timeout/nosignal-0.php
執行結果總是 "cURL Error (28): Timeout was reached"
<?php
if (!isset($_GET['foo'])) {
    // Client
    $ch = curl_init('http://demo.cinc.biz/curl_test_timeout/nosignal-0.php?foo=bar');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_NOSIGNAL, 0);  // CURLOPT_NOSIGNAL 設為 0
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 900); // 設定最長執行 900 毫秒
    $data = curl_exec($ch);
    $curl_errno = curl_errno($ch);
    $curl_error = curl_error($ch);
    curl_close($ch);

if ($curl_errno > 0) {
    echo "cURL Error ($curl_errno): $curl_error\n";
} else {
    echo "Data received: $data\n";
}
} else {
    // Server
    // sleep(10);
    echo "Done.";
}


測試 2:
將 CURLOPT_NOSIGNAL 設為 1
測試網頁:http://demo.cinc.biz/curl_test_timeout/nosignal-1.php
執行結果如果沒逾時,會得到 "Data received: Done. "
<?php
if (!isset($_GET['foo'])) {
    // Client
    $ch = curl_init('http://demo.cinc.biz/curl_test_timeout/nosignal-1.php?foo=bar');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_NOSIGNAL, 1); // CURLOPT_NOSIGNAL 設為 1
    curl_setopt($ch, CURLOPT_TIMEOUT_MS, 900); // 設定最長執行 900 毫秒
    $data = curl_exec($ch);
    $curl_errno = curl_errno($ch);
    $curl_error = curl_error($ch);
    curl_close($ch);

    if ($curl_errno > 0) {
        echo "cURL Error ($curl_errno): $curl_error\n";
    } else {
        echo "Data received: $data\n";
    }
} else {
    // Server
    // sleep(10);
    echo "Done.";
}

沒有留言:

張貼留言