网络连接的各种timeout
TCP
SERVER端DROP SYNC报文导致的重试
tcp sync 报文默认重试6次,每次等待的时间逐渐变长,最长等待 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 = 127 s
测试步骤
iptables 添加规则,drop掉TCP的sync包
1 | [qisheng.li@YD-Test-01 server]$ sudo iptables -A INPUT --protocol tcp --dport 7777 --syn -j DROP |
简单启动一个server,监听7777端口:
1 | [qisheng.li@YD-Test-01 server]$ nc -l 7777 & |
tcpdump抓包,指定port 7777:
1 | [qisheng.li@YD-Test-01 server]$ sudo tcpdump -i lo -Ss0 -n src 127.0.0.1 and dst 127.0.0.1 and port 7777 |
客户端超时时间:
1 | [qisheng.li@YD-Test-01 server]$ time telnet localhost 7777 |
做差得到重试的间隔, telnet总共请求了2m7.257s
,从而可以倒推出最后一次等待的时间
时间 | 时间差 |
---|---|
19:36:44.372996 | |
19:36:45.375615 | 0:00:01.01 (1s) |
19:36:47.379606 | 0:00:02.02 (2s) |
19:36:51.387627 | 0:00:04.04 (4s) |
19:36:59.403615 | 0:00:08.08 (8s) |
19:37:15.435623 | 0:00:16.16 (16s) |
19:37:47.499624 | 0:00:32.32 (32s) |
0:01:04.254 (64s) |
然后就超时了。
查看系统配置的超时重试次数:
1 | [qisheng.li@YD-Test-01 server]$ sudo sysctl -a | grep tcp_syn_retries |
带上第一次请求,正好七次跟我们的观测一致。
恢复iptables
1 | [qisheng.li@YD-Test-01 server]$ sudo iptables --list --line-numbers |
删除对应的规则
1 | [qisheng.li@YD-Test-01 server]$ sudo iptables -D INPUT 1 |
再试下telnet就好了
1 | [qisheng.li@YD-Test-01 server]$ time telnet localhost 7777 |
已经ok了。
Client端drop掉Server的ack导致的重试
- Client(yd-test-01):
- ip: 192.168.16.211
- Iptables:
sudo iptables -A INPUT --protocol tcp --sport 7777 -j DROP
- Server(yd-test-02):
- Ip: 192.168.16.213
- Nc:
nc -l 7777
- Tcpdump:
sudo tcpdump -i eth0 -s0 -n src port 7777 or dst port 7777
client 端输出:
1 | [qisheng.li@YD-Test-01 server]$ time telnet 192.168.16.213 7777 |
server端抓包输出:
1 | [qisheng.li@yd-test-02 server]$ sudo tcpdump -i eth0 -s0 -n src port 7777 or dst port 7777 |
恢复iptables
别忘了把iptables里的规则删除!
不存在的ip和端口导致的重试
macos
1 | ➜ ~ ping 192.168.16.211 |
等待了75s左右,和macOS系统的net.inet.tcp.keepinit
一致。抓包结果:
1 | ➜ ~ sudo tcpdump -i en0 -Ss0 -n dst 192.168.16.211 and dst port 5555 |
等待的时间和重试的间隔和上面的结论完全不同,吓得我赶紧在centos上测试了下。
centos
但是在centos
上,测试的结果如下:
1 | [qisheng.li@YD-Test-01 server]$ ping 192.168.50.201 |
等待了大约127
s,跟系统配置的重试次数产生的时间吻合。tcpdump的结果:
1 | [qisheng.li@YD-Test-01 server]$ sudo tcpdump -i en0 -Ss0 -n dst 192.168.50.201 and dst port 5555 |
可以发现两个系统的实现上还是有略微的差异的。
java的connection timeout
不设置超时
1 |
|
访问一个没有被监听的端口5555
,经过一段时间之后,抛出异常:
1 | System.currentTimeMillis() - start = 75226 |
大约75s之后,操作超时了,这与macos系统默认的socket超时是一致的:
net.inet.tcp.keepinit = timeout for establishing syn
1 | ➜ ~ sudo sysctl -a | grep "net.inet.tcp.keepinit" |
修改这个值为33000
1 | ➜ ~ sudo sysctl -w net.inet.tcp.keepinit=33000 |
重新跑上面的代码:
1 | System.currentTimeMillis() - start = 33117 |
结论:超时时间也变了,所以没有设置超时的时候用的就是os level的超时时间。
设置超时时间
1 |
|
代码的超时设置为35000
毫秒,系统超时33000
毫秒得到的结果,大概33秒之后就超时了:
1 | System.currentTimeMillis() - start = 33271 |
修改代码超时为30000
毫秒,小于系统默认的33000
毫秒,得到的输出:
1 | System.currentTimeMillis() - start = 30005 |
30秒左右就超时了,说明设置生效。
结论:代码里设置的连接超时,大于系统的默认超时是没有用的,系统会先抛出异常;
小于系统的超时是可以生效的。
实现一瞥
socket read超时
对端宕机时,我们有数据需要发送:
运行起来一个echo服务:
1 | [qisheng.li@YD-Test-01 ~]$ ncat -l 2000 -k -c 'xargs -n1 echo "-->"' |
客户端:
1 | [qisheng.li@YD-Test-01 ~]$ nc localhost 2000 |
建立连接之后,输入hello
, 自动返回了--> hello
然后利用iptables模拟网络故障,并抓包:
1 | sudo iptables -A INPUT --protocol tcp --dport 2000 -j DROP |
客户端再次输入:
1 | [qisheng.li@YD-Test-01 ~]$ nc localhost 2000 |
此时hang住了, 直到超时,大约持续了 13 min(22:33:21.427677 ~ 22:46:53.578945)
1 | [qisheng.li@YD-Test-01 ~]$ nc localhost 2000 |
抓包结果:
1 | [qisheng.li@YD-Test-01 ~]$ sudo tcpdump -i lo -Ss0 -n src 127.0.0.1 and dst 127.0.0.1 and port 2000 |
内核相关参数:
1 | [qisheng.li@YD-Test-01 ~]$ cat /proc/sys/net/ipv4/tcp_retries1 |
别忘了删除iptables的规则。
对端宕机时,我们没有数据需要发送:
内核相关参数:
1 | [qisheng.li@YD-Test-01 ~]$ sudo sysctl -a | grep "tcp_keepalive" |
setup好上面的client和server,