惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
E
Exploit-DB.com RSS Feed
SecWiki News
SecWiki News
Forbes - Security
Forbes - Security
N
News | PayPal Newsroom
S
Security @ Cisco Blogs
Schneier on Security
Schneier on Security
V
V2EX - 技术
S
Secure Thoughts
W
WeLiveSecurity
Google DeepMind News
Google DeepMind News
C
CERT Recently Published Vulnerability Notes
NISL@THU
NISL@THU
S
Securelist
S
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
V
Vulnerabilities – Threatpost
Security Latest
Security Latest
Recent Commits to openclaw:main
Recent Commits to openclaw:main
G
GRAHAM CLULEY
H
Hacker News: Front Page
Microsoft Azure Blog
Microsoft Azure Blog
I
Intezer
Google Online Security Blog
Google Online Security Blog
美团技术团队
阮一峰的网络日志
阮一峰的网络日志
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Webroot Blog
Webroot Blog
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
P
Proofpoint News Feed
The Cloudflare Blog
I
InfoQ
L
LangChain Blog
U
Unit 42
P
Proofpoint News Feed
S
Schneier on Security
S
Security Affairs
Y
Y Combinator Blog
T
Tenable Blog
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog
量子位
Google DeepMind News
Google DeepMind News
Cyberwarzone
Cyberwarzone
博客园 - 聂微东
D
Darknet – Hacking Tools, Hacker News & Cyber Security
GbyAI
GbyAI
AWS News Blog
AWS News Blog

风雪之隅

深入理解PHP7内核之OBJECT - 风雪之隅 PHP 8新特性之Attributes(注解) - 风雪之隅 博客迁移到腾讯云 - 风雪之隅 在Qcon 2015 北京上的演讲PPT - PHP7 Yar-2.1 新功能介绍 - 风雪之隅 Yaf and Phalcon, which is faster? HTTPOXY漏洞说明 - 风雪之隅 一个关于Zend O+的小分享 - 风雪之隅 在PHP中使用协程实现多任务调度 - 风雪之隅 PHP8.0的Named Parameter - 风雪之隅 关于PHP,关于Realsee - 风雪之隅 PHP8新特性之match表达式 - 风雪之隅 PHP 8新特性之JIT简介 - 风雪之隅 使用SSE2指令高效实现strtolower - 风雪之隅 使用OSC52实现iTerm2远程pbcopy - 风雪之隅 Yaf 3.2 发布 - 风雪之隅 使用PHP Socket开发Yar TCP服务 - 风雪之隅 Yac 2.1 升级说明 - 风雪之隅 Yaf-3.1 10%性能提升版 - 风雪之隅 Yaconf-1.1 40%速度提升版 - 风雪之隅 PHP FFI详解 - 一种全新的PHP扩展方式 - 风雪之隅 使用SSE2指令集加速字符替换 - 风雪之隅 Yaf_Loader重构测试 - 风雪之隅 PHP_INT_MIN 和 -9223372036854775808 - 风雪之隅 深入理解PHP7内核之FAST_ZPP - 风雪之隅 深入理解PHP7内核之HashTable - 风雪之隅 var_dump(1...9)输出什么? - 风雪之隅 使用内存硬盘(tmpfs)来加速你的网站 - 风雪之隅 print不是函数 - 风雪之隅 令人困惑的strtotime - 风雪之隅 深入理解PHP7内核之Reference - 风雪之隅 深入理解PHP7内核之zval - 风雪之隅 PHP的性能演进(从PHP5.0到PHP7.1的性能全评测) - 风雪之隅 让PHP7达到最高性能的几个Tips - 风雪之隅 写在PHP7发布之际的一些话 - 风雪之隅 让你的PHP7更快之Hugepage - 风雪之隅 让你的PHP7更快(GCC PGO) - 风雪之隅 Yaconf - 一个高性能的配置管理扩展 - 风雪之隅 记录一场没有胜利的局部战斗 - 风雪之隅 PHP7 VS HHVM (Wordpress) - 风雪之隅 GCC优化引起的一个"问题" - 风雪之隅 Weibo LAMP演变 - 6月在上海分享的PPT - 风雪之隅 一个小玩意PHP-Valgrind的介绍 - 风雪之隅 PHP浮点数的一个常见问题的解答 - 风雪之隅 Yac (Yet Another Cache) - 无锁共享内存Cache PDOStatement::bindParam的一个陷阱 - 风雪之隅 Mcrypt响应慢的一个原因 - 风雪之隅 一个程序员眼中的价值 - 风雪之隅 一个关于if else容易迷惑的问题 - 风雪之隅
Curl的毫秒超时的一个"Bug" - 风雪之隅
Prestige par · 2023-07-07 · via 风雪之隅

最近我们的服务在升级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了 🙂
PS, 为什么冠以"Bug", 我只是好奇, 他们为什么不用setitimer?
参考: http://stackoverflow.com/questions/7987584/curl-timeout-less-than-1000ms-always-fails