fasionchan

读万卷书,行万里路,品万味肴,撸万行码。

谈谈TCP TIME_WAIT状态

| Comments

学过TCP协议的童鞋对以下状态变迁图应该不陌生,本文着重讨论TIME_WAIT状态。

从图中可以清楚看到:主动关闭的一端最终会进入TIME_WAIT状态,并维持2*MSL时间,一般情况下为240秒。 那么,为什么需要TIME_WAIT状态呢?为什么需要持续2*MSL时长呢?其中有什么特别的考虑吗?

作用

从上图可以看到,进入TIME_WAIT状态前,收到对端FIN包并且回复ACK包。 网络是不可靠的,也就是说最后这个ACK包可能被中间链路丢掉了。 对端协议栈在还有收到ACK包的情况下,将重传FIN包,本端需要对FIN包进行相应。 如果此时连接资源已经回收,系统只能响应RST包,这时对端就会认为是异常退出。 因此,保留TIME_WAIT状态套接字并维持一段时间,就可以正确响应对端重传的FIN包,让TCP连接优雅关闭。

另一场景是,对端可能还有FIN包还在网络逗留(延迟)。 如果新连接复用旧连接的所有要素(包括地址-端口对、TCP序列号),旧连接迟到的FIN终止新连接! 当然了,这个场景出现的概率比较低,但还是存在。

问题

当一个系统主动关闭大量TCP连接时,由于2MSL存在,将产生大量的TIME_WAIT状态连接。 TCP连接需要消耗一定的系统资源,因此,极端情况下将导致活跃连接响应速度下降甚至停止服务

发起主动关闭的一端一般是客户端,这时候意味着服务端可以高枕无忧呢?

肯定不是的。服务端也有很多场景会发起主动关闭,最常见的应该是类似nginx之类的反向代理了。

如上图,Web服务经常按照业务逻辑进行划分并独立部署,前端采用Nginx做统一接入以及负载均衡。 对Web服务器来说(黄色部分),Nginx服务器为客户端。在Nginx转发请求完成后,主动关闭不可避免。 这样,当系统存在高并发短连接请求时,Nginx服务器上有大量TIME WAIT状态连接存在也就不奇怪了。

解决方案

开启SYN Cookies

SYN队列溢出时,采用Cookie来处理,继续接受新连接。 SYN Cookie可以在一定程度防范SYN Flood攻击,详情请参看这里。 配置选项为net.ipv4.tcp_syncookies,可以采用sysctl或者proc伪文件系统两种方式设置。

sysctl方式。编辑文件/etc/sysctl.conf,调整或增加以下行:

1
net.ipv4.tcp_syncookies = 1

调整完成后,运行命令sysctl -p即可生效。

proc伪文件系统方式

1
2
3
4
5
6
7
8
9
# 查看当前值
$ cat /proc/sys/net/ipv4/tcp_syncookies
0

# 开启
$ echo 1 > /proc/sys/net/ipv4/tcp_syncookies

# 关闭
$ echo 0 > /proc/sys/net/ipv4/tcp_syncookies

开启TIME_WAIT重用

开启net.ipv4.tcp_tw_reuse选项,将允许将TIME_WAIT状态socket重新用于新的TCP连接。 设置方式也可用通过sysctlproc伪文件系统方式,请参考上节,下文不在赘述。

开启TIME_WAIT快速回收

开启net.ipv4.tcp_tw_recycle选项,快速回收处于TIME_WAIT状态的socket

修改FIN超时时间

修改net.ipv4.tcp_fin_timeout选项。

扩大外连端口范围

修改net.ipv4.ip_local_port_range选项。这个选项表示向外连接的端口范围。 当然了,扩大范围只能缓解问题,无法彻底解决,另外端口也存在最大值65000左右。

net.ipv4.tcp_max_syn_backlognet.ipv4.tcp_max_tw_bucketsnet.ipv4.tcp_keepalive_time

设置SO_LINGER选项

Comments