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-out,Code爲28。但在另一些環境下,則能夠正常請求並響應。this
爲了解決上面的疑問,分別進行了如下調試排查。url
經過使用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配置
發現,雖然版本同樣,但各選項開關配置不一樣,並且使用的協議也不同。
繼續使用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?),從而保證了請求過程。
關於send,recv,sendto,recvfrom之間的區別,可查看:
http://blog.csdn.net/liangkaiyang/article/details/5931901
1、對於擴展、底層和第三方,在使用前須明確各調用的原理,及各參數的說明。
2、當遇到問題時,能夠經過各類工具進行調試、快速定位問題。
最後,開發過程當中,請相信:修復一個BUG,會帶來另一個BUG。並且,你可能永遠不知一行代碼值多少錢,直到線上發現了問題並致使了損失。