curl超时导致进程假死

其实主要是如何去查看进程相关的东西

很多时候的进程假死都是网络超时导致的,比如curl。
curl的底层库是libcurl,它有以下几个对时间控制的设置:

1
2
3
4
5
6
CURLOPT_FTP_RESPONSE_TIMEOUT: No default (indefinite)
CURLOPT_TIMEOUT: No default (indefinite)
CURLOPT_TIMEOUT_MS: No default (indefinite)
CURLOPT_CONNECTTIMEOUT: Defaults to 300 seconds
CURLOPT_CONNECTTIMEOUT_MS: No default
CURLOPT_ACCEPTTIMEOUT_MS: Defaults to 60000 ms

其中 CURLOPT_ACCEPTTIMEOUT_MS是和ftp相关的,CURLOPT_CONNECTTIMEOUT是连接的超时设置。可以从https://curl.haxx.se/libcurl/c/curl_easy_setopt.html去查看相关参数。
可以看出,默认情况下,curl是不会超时的,假如连上服务,curl会一直保持连接。
对于php,可以从http://cn2.php.net/manual/zh/function.curl-setopt.php 查看。
我们来模拟一下场景:
在一台机器a上用nc开一个123的端口去等待连接

然后在另一台机器b上用curl命令连接

可以看到它会阻塞在那,此时用strace命令查看curl进程,看该进程到底在干啥

发现curl一直在用poll并且超时。
通常来说,我们查问题是从strace开始看的,所以找到该处,看到是poll,并且超时,而它打开的文件描述符fd是3,那么接下来用lsof查看该进程打开的所有fd

找到fd是3的一行,图中是3u,看到是一个tcp连接,连接的对方ip正是机器a的ip。

curl能设置毫秒级别的超时吗?虽然curl提供了相关ms参数设置,但实际上却是还有问题,具体参考鸟哥的这篇文章http://www.laruence.com/2014/01/21/2939.html
php官方文档也对CURLOPT_TIMEOUT_MS给出了说明:

设置cURL允许执行的最长毫秒数。 如果 libcurl 编译时使用系统标准的名称解析器( standard system name resolver),那部分的连接仍旧使用以秒计的超时解决方案,最小超时时间还是一秒钟。

还是在机器a上起123端口,然后机器b上用php去测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

function test($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10);
$result = curl_exec($ch);
var_dump(curl_error($ch));
curl_close($ch);
return $result;
}

$url = 'http://x.x.x.x:123';
$start = gettimeofday(true);
$l = test($url);
var_dump($l);
$time = gettimeofday(true) - $start;
var_dump($time);

执行返回:Timeout was reached,并且在a上没接收到任何请求
一种是设置CURLOPT_NOSIGNAL

1
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);

此时再执行,会发现a上确实接收到请求了,但是curl超时最小1秒,这和官网上说的一致,也就是设置毫秒无效。

另外一种是启动libcurl的c-ares,这是异步dns解析,一般linux机器上安装curl都不会启动该功能,可以在自己机器上用curl –version查看下是否有c-ares标志,如需启用需要下载相关安装包,并且重新编译libcurl,可以参考https://www.haiyun.me/archives/1068.html

参考资料:

https://stackoverflow.com/questions/10308915/php-default-curl-timeout-value