4年前PHP Curl毫秒超時(shí)問題現(xiàn)在怎么樣了?

江湖慣例,先上結(jié)論:

  • PHP 7和libcurl 7.60.0對(duì)curl毫秒超時(shí)理論都是支持的毙籽,但如果DNS使用同步解析模式(部分Linux發(fā)行版缺省為依賴alram信號(hào)的同步模式),則無(wú)法支持毫秒
  • 通過設(shè)置CURLOPT_NOSIGNAL(Guzzle毫秒的缺省處理)毡庆,會(huì)設(shè)置DNS解析不超時(shí)坑赡,一旦DNS異常將導(dǎo)致服務(wù)阻塞,簡(jiǎn)單粗暴風(fēng)險(xiǎn)高
  • 建議方案:配置curl時(shí)啟用異步DNS解析特性么抗,兩種異步DNS解析特性均可
    • ./configure --enable-ares:開啟c-ares
    • ./configure --enable-threaded-resolver:開啟threaded-resolver
    • 二者對(duì)比說明:libcurls-name-resolving
  • CentOS 7中缺省編譯的Curl已配置異步DNS解析毅否,也就說CentOS7下PHP 7 完美支持毫秒超時(shí)

鳥哥在14年拋出的問題

問題起源:Laruence:21 Jan 14 Curl的毫秒超時(shí)的一個(gè)”Bug”

問題描述:

libcurl新版本支持毫秒級(jí)超時(shí),升級(jí)PHP后蝇刀,設(shè)置毫秒超時(shí)直接返回超時(shí)錯(cuò)誤(Timeout reached 28

解決方案:

  • 方案1:
    • curl_setopt($ch, CURLOPT_NOSIGNAL, 1);禁用信號(hào)螟加,直接跳過DNS解析超時(shí)校驗(yàn)
    • 缺點(diǎn):DNS解析不受控制,如果DNS服務(wù)異常吞琐,且無(wú)超時(shí)捆探,導(dǎo)致整個(gè)應(yīng)用大面積阻塞
  • 方案2:
    • libcurl編譯時(shí)啟用c-ares進(jìn)行DNS解析,如此配置./configure --enable-ares[=PATH]
    • 缺點(diǎn):依賴libcurl編譯時(shí)配置

PHP文檔中關(guān)于TIMEOUT_MS說明

看下PHP文檔里的說明:http://php.net/manual/zh/function.curl-setopt.php

  • CURLOPT_CONNECTTIMEOUT_MS
  • 嘗試連接等待的時(shí)間站粟,以毫秒為單位黍图。設(shè)置為0,則無(wú)限等待卒蘸。 如果 libcurl 編譯時(shí)使用系統(tǒng)標(biāo)準(zhǔn)的名稱解析器( standard system name resolver)雌隅,那部分的連接仍舊使用以秒計(jì)的超時(shí)解決方案翻默,最小超時(shí)時(shí)間還是一秒鐘。
  • 在 cURL 7.16.2 中被加入恰起。從 PHP 5.2.3 開始可用修械。
  • The number of milliseconds to wait while trying to connect. Use 0 to wait indefinitely. If libcurl is built to use the standard system name resolver, that portion of the connect will still use full-second resolution for timeouts with a minimum timeout allowed of one second.
  • Added in cURL 7.16.2. Available since PHP 5.2.3.

2018年的今天這個(gè)問題如何

結(jié)論:

  • curl-7.60.0中同樣保持此段代碼,原因在于使用Linux默認(rèn)的SIGALARM進(jìn)行DNS解析時(shí)检盼,alarm()最小時(shí)間為1秒
  • 因此缺省情況下肯污,直接使用毫秒仍然會(huì)有問題
  • curl-7.60.0.tar.gz中相關(guān)部分代碼如下:
int Curl_resolv_timeout(struct connectdata *conn,
                        const char *hostname,
                        int port,
                        struct Curl_dns_entry **entry,
                        time_t timeoutms)
{
...
...
#ifdef USE_ALARM_TIMEOUT
  if(data->set.no_signal)  // 設(shè)置no_signal,則timeout為0吨枉,忽略超時(shí)
    /* Ignore the timeout when signals are disabled */
    timeout = 0;
  else
    timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;

  if(!timeout)  // timeout=0蹦渣,則無(wú)超時(shí)解析
    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
    return Curl_resolv(conn, hostname, port, entry);

  if(timeout < 1000) { // 如果timeout < 1000,則直接返回CURLRESOLV_TIMEDOUT超時(shí)貌亭,增加了日志輸出
    /* The alarm() function only provides integer second resolution, so if
       we want to wait less than one second we must bail out already now. */
    failf(data,
        "remaining timeout of %ld too small to resolve via SIGALRM method",
        timeout);
    return CURLRESOLV_TIMEDOUT;
  }

Guzzle Http中是否有規(guī)避策略

  • guzzlehttp/guzzle 6.2
    • 如果設(shè)置timeout時(shí)間小于1柬唯,則guzzle會(huì)設(shè)置CURLOPT_NOSIGNAL=true
    • 代碼如下:
$timeoutRequiresNoSignal = false;
if (isset($options['timeout'])) {
    $timeoutRequiresNoSignal |= $options['timeout'] < 1;  // 超時(shí)時(shí)間小于1,則設(shè)置NoSignal=1
    $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
}
// CURL default value is CURL_IPRESOLVE_WHATEVER
if (isset($options['force_ip_resolve'])) {
    if ('v4' === $options['force_ip_resolve']) {
        $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
    } else if ('v6' === $options['force_ip_resolve']) {
        $conf[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V6;
    }
}
if (isset($options['connect_timeout'])) {
    $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; // 超時(shí)時(shí)間小于1圃庭,則設(shè)置NoSignal=1
    $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
}
if ($timeoutRequiresNoSignal && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
    $conf[CURLOPT_NOSIGNAL] = true;
}
  • 在某個(gè)老版本guzzle中發(fā)現(xiàn)并未兼容的老代碼:
if (isset($options['timeout'])) {
    $conf[CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000;
}
if (isset($options['connect_timeout'])) {
    $conf[CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000;
}

CentOS 7中curl

CentOS 7中缺省攜帶的curl編譯已配置threaded-resolver锄奢,也就是說CentOS 7 + PHP 7組合使用毫秒超時(shí)很安全。

補(bǔ)充一點(diǎn)curl支持異步DNS的方式有兩種:c-ares和threaded-resolver(centos 7中缺省啟用threaded-resolver)

附帶兩個(gè)查看本機(jī)curl編譯配置的命令:

  • curl --version:AsynchDNS表示支持異步DNS特性
  • curl-config --configure:查看編譯時(shí)配置

附錄不同CentOS下curl缺省編譯配置:

  • CentOS 6.5
>curl --version

curl 7.37.0 (x86_64-unknown-linux-gnu) libcurl/7.37.0 OpenSSL/1.0.1e zlib/1.2.3
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smtp smtps telnet tftp 
Features: Largefile NTLM NTLM_WB SSL libz

> curl-config --configure

'--with-ssl' '--with-ipv6' '--enable-ldap' '--enable-ldaps'
  • CentOS 7
> cat /etc/redhat-release
CentOS Linux release 7.0.1406 (Core) 

> curl --version
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.19.1 Basic ECC zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp 
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz

> curl-config --configure

'--build=x86_64-redhat-linux-gnu' '--host=x86_64-redhat-linux-gnu' '--program-prefix=' '--disable-dependency-tracking' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib64' '--libexecdir=/usr/libexec' '--localstatedir=/var' '--sharedstatedir=/var/lib' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--disable-static' '--enable-hidden-symbols' '--enable-ipv6' '--enable-ldaps' '--enable-manual' '--enable-threaded-resolver' '--with-ca-bundle=/etc/pki/tls/certs/ca-bundle.crt' '--with-gssapi' '--with-libidn' '--with-libssh2' '--without-ssl' '--with-nss' 'build_alias=x86_64-redhat-linux-gnu' 'host_alias=x86_64-redhat-linux-gnu' 'CFLAGS=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic' 'LDFLAGS=-Wl,-z,relro '

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剧腻,一起剝皮案震驚了整個(gè)濱河市拘央,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌书在,老刑警劉巖灰伟,帶你破解...
    沈念sama閱讀 219,589評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異儒旬,居然都是意外死亡栏账,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門义矛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)发笔,“玉大人,你說我怎么就攤上這事凉翻。” “怎么了捻激?”我有些...
    開封第一講書人閱讀 165,933評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵制轰,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我胞谭,道長(zhǎng)垃杖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評(píng)論 1 295
  • 正文 為了忘掉前任丈屹,我火速辦了婚禮调俘,結(jié)果婚禮上伶棒,老公的妹妹穿的比我還像新娘。我一直安慰自己彩库,他們只是感情好肤无,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,999評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骇钦,像睡著了一般宛渐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上眯搭,一...
    開封第一講書人閱讀 51,775評(píng)論 1 307
  • 那天窥翩,我揣著相機(jī)與錄音,去河邊找鬼鳞仙。 笑死寇蚊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的棍好。 我是一名探鬼主播幔荒,決...
    沈念sama閱讀 40,474評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼梳玫!你這毒婦竟也來(lái)了爹梁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,359評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤提澎,失蹤者是張志新(化名)和其女友劉穎姚垃,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盼忌,經(jīng)...
    沈念sama閱讀 45,854評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡积糯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,007評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谦纱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片看成。...
    茶點(diǎn)故事閱讀 40,146評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖跨嘉,靈堂內(nèi)的尸體忽然破棺而出川慌,到底是詐尸還是另有隱情,我是刑警寧澤祠乃,帶...
    沈念sama閱讀 35,826評(píng)論 5 346
  • 正文 年R本政府宣布梦重,位于F島的核電站,受9級(jí)特大地震影響亮瓷,放射性物質(zhì)發(fā)生泄漏琴拧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,484評(píng)論 3 331
  • 文/蒙蒙 一嘱支、第九天 我趴在偏房一處隱蔽的房頂上張望蚓胸。 院中可真熱鬧挣饥,春花似錦、人聲如沸沛膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)于置。三九已至茧吊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間八毯,已是汗流浹背搓侄。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留话速,地道東北人讶踪。 一個(gè)月前我還...
    沈念sama閱讀 48,420評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像泊交,于是被迫代替她去往敵國(guó)和親乳讥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,107評(píng)論 2 356

推薦閱讀更多精彩內(nèi)容

  • 一廓俭、什么是CURL云石? cURL 是一個(gè)利用URL語(yǔ)法規(guī)定來(lái)傳輸文件和數(shù)據(jù)的工具,支持很多協(xié)議研乒,如HTTP汹忠、FTP、...
    伊Summer閱讀 1,273評(píng)論 0 4
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理雹熬,服務(wù)發(fā)現(xiàn)宽菜,斷路器,智...
    卡卡羅2017閱讀 134,672評(píng)論 18 139
  • 一竿报、什么是CURL铅乡? cURL 是一個(gè)利用URL語(yǔ)法規(guī)定來(lái)傳輸文件和數(shù)據(jù)的工具,支持很多協(xié)議烈菌,如HTTP阵幸、FTP、...
    茶藝瑤閱讀 4,602評(píng)論 0 6
  • 概述 在PHP開發(fā)中工作里非常多使用到超時(shí)處理到超時(shí)的場(chǎng)合僧界,我說幾個(gè)場(chǎng)景: 異步獲取數(shù)據(jù)如果某個(gè)后端數(shù)據(jù)源獲取不成...
    FrancisSoung閱讀 921評(píng)論 2 12
  • 這首歌一開始悠揚(yáng)的旋律響起捂襟,充滿著溫暖的力量,歌聲似流水一般緩緩地傾瀉欢峰,淌入我的心靈深處葬荷。 歲月的風(fēng)霜飄在了母愛的...
    最美的太陽(yáng)93閱讀 278評(píng)論 5 4