: jank : : 3692 : 2019-07-29 15:29 linux
一、事故起源:
1.公司有个golang http服务,通过nginx 做代理,当用户访问巨大的时候,服务端出现了大量的TIME_WAIT 。
2.通过netstat 查看:
# netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key," ",state[key]}'
二、造成TIME_WAIT 状态的原因:
首先我们应该要弄清楚tcp关闭连接的四次握手原理:
如上图所示:
1.Client向Server发送FIN包,表示Client主动要关闭连接,然后进入FIN_WAIT_1状态,等待Server返回ACK包。此后Client不能再向Server发送数据,但能读取数据。
2.Server收到FIN包后向Client发送ACK包,然后进入CLOSE_WAIT状态,此后Server不能再读取数据,但可以继续向Client发送数据。
3.Client收到Server返回的ACK包后进入FIN_WAIT_2状态,等待Server发送FIN包。
4.Server完成数据的发送后,将FIN包发送给Client,然后进入LAST_ACK状态,等待Client返回ACK包,此后Server既不能读取数据,也不能发送数据。
5.Client收到FIN包后向Server发送ACK包,然后进入TIME_WAIT状态,接着等待足够长的时间(2MSL)以确保Server接收到ACK包,最后回到CLOSED状态,释放网络资源。
6.Server收到Client返回的ACK包后便回到CLOSED状态,释放网络资源。
三、处理方案:
1.通过更改系统配置:
调整TIME_WAIT超时时间
vi /etc/sysctl.conf
#表示开启重用。 允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1
#生效,如下命令
/sbin/sysctl -p
如上通过更改系统配置重用,可能会带来一些潜在的故障:
Linux是否启用这种行为取决于tcp_timestamps和tcp_tw_recycle,因为tcp_timestamps缺省就是开启的,所以当tcp_tw_recycle被开启后,实际上这种行为就被激活了,当客户端或服务端以NAT方式构建的时候就可能出现问题,下面以客户端NAT为例来说明:
当多个客户端通过NAT方式联网并与服务端交互时,服务端看到的是同一个IP,也就是说对服务端而言这些客户端实际上等同于一个,可惜由于这些客户端的时间戳可能存在差异,于是乎从服务端的视角看,便可能出现时间戳错乱的现象,进而直接导致时间戳小的数据包被丢弃。如果发生了此类问题,具体的表现通常是是客户端明明发送的SYN,但服务端就是不响应ACK.
安全起见,通常要禁止tcp_tw_recycle。说到这里,大家可能会想到另一种解决方案:把tcp_timestamps设置为0,tcp_tw_recycle设置为1,这样不就可以鱼与熊掌兼得了么?可惜一旦关闭了tcp_timestamps,那么即便打开了tcp_tw_recycle,也没有效果。
好在我们还有另一个内核参数tcp_max_tw_buckets(一般缺省是180000)可用:
# sysctl net.ipv4.tcp_max_tw_buckets=100000
通过设置它,系统会将多余的TIME_WAIT删除掉,此时系统日志里可能会显示:「TCP: time wait bucket table overflow」,不过除非不得已,否则不要轻易使用。
以上这些方式,只做到了治标不治本,显得还是很牵强。
2.通过nginx <--> 服务端 进行长连接:
nginx 1.1 以后的版本upstream已经支持keep-alive, 官方文档:http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
upstream http_backend { server 127.0.0.1:8080; keepalive 16; }
通过这种方式,TIME_WAIT数明显减少。
参考链接:
https://blog.csdn.net/godleading/article/details/50849253
https://blog.huoding.com/2012/01/19/142