Curl的毫秒超時的一個」Bug」

最近咱們的服務在升級php使用的libcurl, 指望新版本的libcurl支持毫秒級的超時, 從而能夠更加精細的控制後端的接口超時, 從而提升總體響應時間.後端

可是, 咱們卻發現, 在咱們的CentOS服務器上, 當你設置了小於1000ms的超時之後, curl不會發起任何請求, 而直接返回超時錯誤(Timeout reached 28).服務器

原來, 這裏面有一個坑, CURL默認的, 在Linux系統上, 若是使用了系統標準的DNS解析, 則會使用SIGALARM來提供控制域名解析超時的功能, 可是SIGALARM不支持小於1s的超時, 因而在libcurl 7.28.1的代碼中(注意中文註釋行):app

int Curl_resolv_timeout(struct connectdata *conn,
                        const char *hostname,
                        int port,
                        struct Curl_dns_entry **entry,
                        long timeoutms)
{
.......
.......
#ifdef USE_ALARM_TIMEOUT
  if(data->set.no_signal)
    /* Ignore the timeout when signals are disabled */
    timeout = 0;
  else
    timeout = timeoutms;
 
  if(!timeout)
    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    return Curl_resolv(conn, hostname, port, entry);
 
  if(timeout < 1000) //若是小於1000, 直接超時返回
    /* The alarm() function only provides integer second resolution, so if
       we want to wait less than one second we must bail out already now. */
    return CURLRESOLV_TIMEDOUT;
 
  ....
  ....

可見, 當你的超時時間小於1000ms的時候, name解析會直接返回CURLRESOLV_TIMEOUT, 最後會致使CURLE_OPERATION_TIMEDOUT, 而後就Error, Timeout reached了…less

這….太坑爹了吧? 難道說, 咱們就不能使用毫秒超時麼? 那你提供這功能幹啥?curl

仍是看代碼, 仍是剛纔那段代碼, 注意這個(中文註釋行):async

#ifdef USE_ALARM_TIMEOUT
  if(data->set.no_signal)  //注意這行
    /* Ignore the timeout when signals are disabled */
    timeout = 0;
  else
    timeout = timeoutms;
 
  if(!timeout)
    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    return Curl_resolv(conn, hostname, port, entry);
 
  if(timeout < 1000)
    /* The alarm() function only provides integer second resolution, so if
       we want to wait less than one second we must bail out already now. */
    return CURLRESOLV_TIMEDOUT;

看起來, 只要set.no_signal 這個東西爲1, 就能夠繞過了… 那這個玩意是啥呢?ide

這就簡單了, grep一下代碼, 發現:google

 
  case CURLOPT_NOSIGNAL:
    /*
     * The application asks not to set any signal() or alarm() handlers,
     * even when using a timeout.
     */
    data->set.no_signal = (0 != va_arg(param, long))?TRUE:FALSE;
    break;

哈哈, 原來是這貨:

<?php
   curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
?>

加上這個OPT之後, 一切終於正常了!

後記:

這樣一來, 就會有一個隱患, 那就是DNS解析將不受超時限制了, 這在於公司內部來講, 通常沒什麼問題, 可是萬一DNS服務器hang住了, 那就可能會形成應用超時.

那麼還有其餘辦法麼?

有, 那就是Mike提醒的, 咱們可讓libcurl使用c-ares(C library for asynchronous DNS requests)來作名字解析. 具體的能夠在config curl的時候:

相關文章
相關標籤/搜索