• Curl的毫秒超时的一个”Bug”


    Curl的毫秒超时的一个”Bug”
    
    2015-12-15 PHP老杨
    
    最近我们的服务在升级php使用的libcurl, 期望新版本的libcurl支持毫秒级的超时, 从而可以更加精细的控制后端的接口超时, 从而提高整体响应时间.
    
    但是, 我们却发现, 在我们的CentOS服务器上, 当你设置了小于1000ms的超时以后, curl不会发起任何请求, 而直接返回超时错误(Timeout reached 28).
    
    原来, 这里面有一个坑, CURL默认的, 在Linux系统上, 如果使用了系统标准的DNS解析, 则会使用SIGALARM来提供控制域名解析超时的功能, 但是SIGALARM不支持小于1s的超时, 于是在libcurl 7.28.1的代码中(注意中文注释行):
    
    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了…
    
    这….太坑爹了吧? 难道说, 我们就不能使用毫秒超时么? 那你提供这功能干啥?
    
    还是看代码, 还是刚才那段代码, 注意这个(中文注释行):
    
    #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, 就可以绕过了… 那这个玩意是啥呢?
    
    这就简单了, grep一下代码, 发现:
    
     
    
    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的时候:
    
    ./configure --enable-ares[=PATH]
    
    这样就可以不用设置NOSIGNAL了 
    
     
     
  • 相关阅读:
    2018-04-13Java编程夯实学习心得(3)
    2018-03-28JavaScript学习心得
    2018-03-27mysql学习心得
    JavaScript-作用域
    样式切换图
    购物车结算
    Visual Studio Code快捷键操作
    复选框
    win10锁屏界面无法设置隐藏
    轮播图
  • 原文地址:https://www.cnblogs.com/archoncap/p/5048835.html
Copyright © 2020-2023  润新知