php下使用CURL设置超时的问题

php下使用CURL设置超时的问题

 

问题描述:

考虑以下使用php进行 curl调用的场景:

 

$ch = curl_init();

//...

$limitTime = $_SERVER['REQUEST_TIME'];      //误用当前时间当超时时间

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

 

$result = curl_exec($ch);

 

由于误用(可能是业务上)了当时时间为CURL设置超时时间,导致在某些环境下,会一直报Connection time-outCode28。但在另外一些环境下,则可以正常请求并响应。

 

为了解决上面的疑问,分别进行了以下调试排查。

 

PHP层的调试

 

通过使用curl_setopt($handle, CURLINFO_HEADER_OUT, true);curl_getinfo()等函数可获取curl调用的一些相关信息,以便进一步定位问题,其中发现curl_getinfo()获取的内容如下:

 

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的配置:

 

php下使用CURL设置超时的问题

环境一:非法超时设置请求失败的CURL配置

 

php下使用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。而且,你可能永远不知一行代码值多少钱,直到线上发现了问题并导致了损失。


你可能感兴趣的:(php下使用CURL设置超时的问题)