php下使用CURL設置超時的問題

php下使用CURL設置超時的問題php

 

問題描述:

考慮如下使用php進行 curl調用的場景:nginx

 

$ch = curl_init();api

//...curl

$limitTime = $_SERVER['REQUEST_TIME'];      //誤用當前時間當超時時間socket

curl_setopt($this->_ch, CURLOPT_TIMEOUT, $limitTime);函數

 

$result = curl_exec($ch);工具

 

因爲誤用(多是業務上)了當時時間爲CURL設置超時時間,致使在某些環境下,會一直報Connection time-outCode28。但在另一些環境下,則能夠正常請求並響應。this

 

爲了解決上面的疑問,分別進行了如下調試排查。url

 

PHP層的調試

 

經過使用curl_setopt($handle, CURLINFO_HEADER_OUT, true);curl_getinfo()等函數可獲取curl調用的一些相關信息,以便進一步定位問題,其中發現curl_getinfo()獲取的內容以下:spa

 

Array

(

    [url] => http://my.api.com/test

    [content_type] =>

    [http_code] => 0

    [header_size] => 0

    [request_size] => 0

    [filetime] => -1

    [ssl_verify_result] => 0

    [redirect_count] => 0

    [total_time] => 0

    [namelookup_time] => 1.4E-5

    [connect_time] => 0

    [pretransfer_time] => 0

    [size_upload] => 0

    [size_download] => 0

    [speed_download] => 0

    [speed_upload] => 0

    [download_content_length] => 0

    [upload_content_length] => 0

    [starttransfer_time] => 0

    [redirect_time] => 0

)

 

發現其中的connect_time一直爲0,也就是根本沒有發起請求,因此一直失敗,從而報超時。

 

系統環境及CURL擴展排查

但較爲奇怪的是,在另一個系統環境下,非法超時設置能夠獲得正常響應。對比這兩個系統環境下CURL的配置:

 

環境一:非法超時設置請求失敗的CURL配置

 

環境二:一樣是非法超時設置但能夠成功請求的CURL配置

 

發現,雖然版本同樣,但各選項開關配置不一樣,並且使用的協議也不同。

 

STRACE與底層排查

繼續使用strace進一步調試,發現:

 

在非法超時設置請求失敗的CURL配置環境下:

gettimeofday({1403098526, 14335}, NULL) = 0
rt_sigaction(SIGALRM, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGALRM, {0xc36410, [], 0}, NULL, 8) = 0
alarm(1403098525)                       = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
time(NULL)                              = 1403098526
rt_sigaction(SIGALRM, {SIG_DFL, [], 0}, NULL, 8) = 0
alarm(0)                                = 1403098525

環境一:非法下超時請求strace

 

rt_sigaction(SIGALRM, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGALRM, {0xc36410, [], 0}, NULL, 8) = 0
alarm(201)                              = 0
rt_sigaction(SIGALRM, {SIG_DFL, [], 0}, NULL, 8) = 0
alarm(0)                                = 201
gettimeofday({1403098701, 975556}, NULL) = 0
gettimeofday({1403098701, 975572}, NULL) = 0
gettimeofday({1403098701, 975587}, NULL) = 0
send(3, "POST /FrontApi.php?service=ApiDa"..., 284, MSG_NOSIGNAL) = 284
gettimeofday({1403098701, 975679}, NULL) = 0
gettimeofday({1403098701, 975695}, NULL) = 0
poll([{fd=3, events=POLLIN}], 1, 1000)  = 1 ([{fd=3, revents=POLLIN}])
poll([{fd=3, events=POLLIN}], 1, 0)     = 1 ([{fd=3, revents=POLLIN}])
recv(3, "HTTP/1.1 200 OK\r\nServer: nginx/1"..., 16384, MSG_NOSIGNAL) = 242
gettimeofday({1403098701, 996962}, NULL) = 0

環境一:正常下請求strace

 

對比正常與非法的strace,發如今非法超時設置下果真沒有調用 send發起請求。而是調用 sigprocmask()對進程進行了阻塞。

 

百度了一下,查看了此函數的說明。

 

http://baike.baidu.com/link?url=O2jI62g-RwRWOOS53W_m-yQb1vuXsvWQPmvnCzeI0Nw1-g7xe4qSJOWsq0bSnIND2wXBxI1dW8-qYl2-jesNNa

 

用於改變進程的當前阻塞信號集.

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);

 

SIG_BLOCK

該進程新的信號屏蔽字是其當前信號屏蔽字和set指向信號集的並集。set包含了我

們但願阻塞的附加信號。

 

 

在一樣是非法超時設置但能夠成功請求的CURL配置環境下:

 

rt_sigaction(SIGALRM, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGALRM, {0x2b15e0c642a0, [], SA_RESTORER, 0x34d5a302d0}, NULL, 8) = 0
alarm(201)                              = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = -1 EAFNOSUPPORT (Address family not supported by protocol)
open("/etc/hosts", O_RDONLY)            = 6
fcntl(6, F_GETFD)                       = 0
fcntl(6, F_SETFD, FD_CLOEXEC)           = 0
fstat(6, {st_mode=S_IFREG|0644, st_size=8761, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b15e0eb4000
read(6, "#Do not remove the following lin"..., 4096) = 4096
read(6, "7\n192.168.42.146 B2C_DEV_146.cli"..., 4096) = 4096
close(6)                                = 0
munmap(0x2b15e0eb4000, 4096)            = 0
alarm(0)                                = 201
rt_sigaction(SIGALRM, {SIG_DFL, [], SA_RESTORER, 0x34d5a302d0}, NULL, 8) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 771728835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 771829835}) = 0
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 6
fcntl(6, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(6, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
connect(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("192.168.42.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
clock_gettime(CLOCK_MONOTONIC, {15318582, 772324835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 772380835}) = 0
poll([{fd=6, events=POLLOUT}], 1, 1000) = 1 ([{fd=6, revents=POLLOUT}])
clock_gettime(CLOCK_MONOTONIC, {15318582, 772488835}) = 0
getsockopt(6, SOL_SOCKET, SO_ERROR, [5587752452096], [4]) = 0
getpeername(6, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("192.168.42.1")}, [16196877055437045776]) = 0
getsockname(6, {sa_family=AF_INET, sin_port=htons(51687), sin_addr=inet_addr("192.168.42.1")}, [16196877055437045776]) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 772750835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 772838835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 772908835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 772999835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 773058835}) = 0
sendto(6, "POST /test?service=Mconf"..., 283, MSG_NOSIGNAL, NULL, 0) = 283
clock_gettime(CLOCK_MONOTONIC, {15318582, 773350835}) = 0
poll([{fd=6, events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout)
poll([{fd=6, events=POLLIN|POLLPRI}], 1, 0) = 0 (Timeout)
clock_gettime(CLOCK_MONOTONIC, {15318582, 773501835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 774095835}) = 0
clock_gettime(CLOCK_MONOTONIC, {15318582, 774170835}) = 0
poll([{fd=6, events=POLLIN|POLLPRI}], 1, 1000) = 1 ([{fd=6, revents=POLLIN}])
poll([{fd=6, events=POLLIN|POLLPRI}], 1, 0) = 1 ([{fd=6, revents=POLLIN}])
recvfrom(6, "HTTP/1.1 200 OK\r\nServer: nginx/1"..., 16384, 0, NULL, NULL) = 242

環境二:非法下超時請求strace

環境二:正常下請求strace

 

發現,不管正常仍是非法的超時設置,環境二下CURL擴展底層都會使用socket進行通訊(TCP?),從而保證了請求過程。


關於sendrecvsendtorecvfrom之間的區別,可查看:

http://blog.csdn.net/liangkaiyang/article/details/5931901

 

總結:

1、對於擴展、底層和第三方,在使用前須明確各調用的原理,及各參數的說明。

2、當遇到問題時,能夠經過各類工具進行調試、快速定位問題。


最後,開發過程當中,請相信:修復一個BUG,會帶來另一個BUG。並且,你可能永遠不知一行代碼值多少錢,直到線上發現了問題並致使了損失。

相關文章
相關標籤/搜索