curl设置超时时间

Posted by 二田 on 2018-10-28

问题

从程序日志看到访问某个域名报 java.net.SocketTimeoutException: failed: connect timed out to xxx.com
偶尔会超时,手动测试了下,也出现了几次 http code是000,比较奇怪

解决过程

常见的返回码

1)2XX 成功;
2)3XX 重定向;
3)4XX 客户端错误;
4)5XX 服务器端错误;

手动测试结果

1
2
3
4
5
6
$curl -o /dev/null -s -w "http_code %{http_code}\ntime_namelookup %{time_namelookup}ntime_connect %{time_connect}\ntime_starttransfer %{time_starttransfer}\ntime_total %{time_total}" "http://www.54im.com"
http_code:000
time_namelookup:0.000 ——DNS域名解析时间
time_connect:0.000 ——client和server端建立TCP 连接的时间
time_starttransfer:0.000 ——从client发出请求;到web的server 响应第一个字节的时间
time_total:0.000 ——client发出请求;到web的server发送会所有的相应数据的时间

http code 000产生原因

什么情况下会有000情况呢
经过测试发现在curl的时候出现000的情况有如下几种:

1.Failed DNS resolution (6)

1
2
3
4
$ curl -w "%{http_code}\n" http://example.invalid/ ; echo "Exit code: $?"
000
curl: (6) Could not resolve host: example.invalid
Exit code: 6

2.Connection refused (7)

1
2
3
4
$ curl -w "%{http_code}\n" http://localhost:81/ ; echo "Exit code: $?"
000
curl: (7) Failed to connect to localhost port 81: Connection refused
Exit code: 7

3.Connection timed out (28)

1
2
3
4
$ curl -w "%{http_code}\n" -m 5 http://10.255.255.1/ ; echo "Exit code: $?"
000
curl: (28) Connection timed out after 5001 milliseconds
Exit code: 28

4.Server actually returns 000 for some reason (0)

开启一个监听端口:

1
2
3
4
5
nc -l -p 65535 & <<EOF
HTTP/1.1 000 Fake Status Code
Content-Length: 0
Connection: close
EOF

客户端请求:

1
2
3
$ curl -w "%{http_code}\n" http://localhost:65535/ ; echo "Exit code: $?"
000
Exit code: 0

curl增加超时时间

我们遇到的就是code为28的。我开始怀疑是网络环境比较差,我加大了超时时间。
使用curl时,有两个超时时间:一个是连接超时时间,另一个是数据传输的最大允许时间。
连接超时时间用 --connect-timeout 参数来指定,数据传输的最大允许时间用 -m 参数来指定。

1
2
3
--connect-timeout <seconds>
Maximum time in seconds that you allow the connection to the server to take. This only limits the connection phase, once curl has connected this option is of no more use. See also the -m/--max-time option.
If this option is used several times, the last one will be used.

这个是指定连接超时时间。 如果出错, 提示形如:curl: (28) connect() timed out!

1
2
3
-m/--max-time <seconds>
Maximum time in seconds that you allow the whole operation to take. This is useful for preventing your batch jobs from hanging for hours due to slow networks or links going down. See also the --connect-timeout option.
If this option is used several times, the last one will be used.

这个是指定整个过程最大的允许时间。 出错提示如:curl: (28) Operation timed out after 2000 milliseconds with 0 bytes received

例如:
curl --connect-timeout 10 -m 20 "http://XXXXXXX"
连接超时的话,出错提示形如:
curl: (28) connect() timed out!
数据传输的最大允许时间超时的话,出错提示形如:

curl: (28) Operation timed out after 2000 milliseconds with 0 bytes received

依然没有用,我写了循环脚本一直curl这个网站的同时也curl了baidu,发生000的时候,curl百度是没有问题的

说明还是对端网站问题,考虑到我这边网络是NAT,对方系统是否开启了TCP快速回收,或者对单ip频率有限制之类。

tcp_tw_recycle参数

后来和对方运维沟通确实开启了tcp_tw_recycle

先来看看这个参数

net.ipv4.tcp_tw_recycle

tcp_tw_recycle (Boolean; default: disabled; since Linux 2.4)[译者注:来自linux man tcp的描述]
Enable fast recycling of TIME-WAIT sockets. Enabling this option is not recommended since this causes
problems when working with NAT (Network Address Translation).

启用TIME-WAIT状态sockets的快速回收,这个选项不推荐启用。在NAT(Network Address Translation)网络下,会导致大量的TCP连接建立错误。

网上很多linux参数调整指南都建议把这些参数net.ipv4.tcp_tw_recycle 设置1「启用」,用于快速减少在TIME-WAIT状态TCP连接数。

timestamp是什么

还有个timestamp先了解下

什么是timestamp

tcp_timestamps的本质是记录数据包的发送时间。

基本步骤如下:

1.发送方在发送数据时,将一个timestamp(表示发送时间)放在包里面
2.接收方在收到数据包后,在对应的ACK包中将收到的timestamp返回给发送方(echo back)
3.发送发收到ACK包后,用当前时刻now - ACK包中的timestamp就能得到准确的RTT

RTT(Round Trip Time)由三部分组成:链路的传播时间(propagation delay)、末端系统的处理时间、路由器缓存中的排队和处理时间(queuing delay)。

简单来说就是我发送一个数据包,然后对端回一个ack,那么当我接到ack之后,就能计算出从我发送出包到接到过了多久,这个时间就是RTT,所以RTT的变化在一定程度上反应了网络的拥塞程度。

RTO就是tcp在发送一个数据包之后,会启动一个重传定时器,而RTO就是这个定时器的重传时间。 在通俗的讲就是,我一开始预先算个定时器时间,如果你回复了ack那正好,如果没有回复给我ack,然后RTO定时器的时间又到了,那么我就重传。

若没有timestamp就会采用SKB->when的方式计算RTT,基本步骤如下:

1.TCP层在发送出一个SKB时,使用skb->when记录发送出去的时间
2.TCP层在收到SKB数据包的确认时,使用now - skb->when来计算RTT

这时会存在一个缺陷,如当进行如下操作:

1.tcp层首次发送SKB时间是send_time1,然后丢包发生重传,重传一个数据包的时间是send_time2
2.tcp层收到SKB的确认包的时间是recv_time

这样无法判断recv_time对应ACK是确认第一次数据包的发送还是确认第二次重传的数据包。

timestamp存在什么负面影响?

会产生10字节的TCP header开销

什么是recycle

表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭

对客户端和服务器同时起作用,开启后在 3.5*RTO 内回收,RTO 200ms~ 120s 具体时间视网络状况。

什么是Per-host PAWS机制?

在高带宽下,TCP序列号可能在较短的时间内就被重复使用(recycle/wrapped),就可能导致同一条TCP流在短时间内出现序号一样的两个合法的数据包及其确认包!

PAWS机制就是为了应对这一现象设计的,这种机制要求所有来个同一个host IP的TCP数据包的

timestamp值是递增的。当收到一个timestamp值,小于服务端记录的对应值后,则会认为这是一个过期的数据包,然后会将其丢弃。

那么问题就来了

  1. 同时开启tcp_timestamp和tcp_tw_recycle会启用TCP/IP协议栈的per-host的PAWS机制
  2. 经过同一NAT转换后的来自不同真实client的数据流,在服务端看来是于同一host打交道
  3. 虽然经过同一NAT转化,但由于不同真实client会携带各自的timestamp值
    因而无法保证整过NAT转化后的数据包携带的timestamp值严格递增
  4. 当服务器的per-host PAWS机制被触发后,会丢弃timestamp值不符合递增条件的数据包

这个参数在 kernel 的4.12后 tcp_tw_recycle都被去掉了

最后去掉这个配置后,没有再出现过超时情况了。

参考链接

https://www.cnblogs.com/xkops/p/5614366.html
http://perthcharles.github.io/2015/08/27/timestamp-NAT/
https://www.cnxct.com/coping-with-the-tcp-time_wait-state-on-busy-linux-servers-in-chinese-and-dont-enable-tcp_tw_recycle/
http://blog.51cto.com/12814931/2126617