IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

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

风雪之隅 2014-11-23 21:42:10 累计浏览 2,508 次
本机暂存

 最近我们的服务在升级php使用的libcurl, 期望新版本的libcurl支持毫秒级的超时, 从而可以更加精细的控制后端的接口超时, 从而提高整体响应时间.

  但是, 我们却发现, 在我们的CentOS服务器上, 当你设置了小于1000ms的超时以后, curl不会发起任何请求, 而直接返回超时错误(Timeout reached 28).

  原来, 这里面有一个坑,  CURL默认的, 在Linux系统上, 使用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以后, 一切终于正常了!

 最后, 这个我想是Curl的一个bug吧, 只不过我们没想明白他们为啥不用setitimer…

 参考:  http://stackoverflow.com/questions/7987584/curl-timeout-less-than-1000ms-always-fails

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. 使用gettext来支持PHP的多语言 (累计阅读 39,270)
  2. WordPress插件开发 -- 在插件使用数据库存储数据 (累计阅读 29,164)
  3. Paypal接口详细代码(PHP版,非API接口) (累计阅读 19,408)
  4. libcurl的使用总结(二) (累计阅读 15,083)
  5. 我的PHP,Python和Ruby之路 (累计阅读 13,147)
  6. include(“./file.php”)和include(“file.php”)区别 (累计阅读 12,789)
  7. 15个最好的免费开源电子商务平台 (累计阅读 12,541)
  8. Redis消息队列的若干实现方式 (累计阅读 12,088)
  9. 到底什么是MVC? (累计阅读 11,867)
  10. 整理了一份招PHP高级工程师的面试题 (累计阅读 11,708)