首页 > php教程 > php手册 > 正文

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

php中文网
发布: 2016-06-13 09:43:34
原创
1079人浏览过

最近我们的服务在升级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
  •     /* 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 
  •     /* 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;

哈哈, 原来是这货:

登录后复制
登录后复制
登录后复制
登录后复制
  •    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了 

PS, 为什么冠以”Bug”, 我只是好奇, 他们为什么不用setitimer?

参考: http://stackoverflow.com/questions/7987584/curl-timeout-less-than-1000ms-always-fails
   作者: Laruence
   本文地址: http://www.laruence.com/2014/01/21/2939.html

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号