Linux TCP 状态 TIME_WAIT 过多的处理详解

| 选择喜欢的代码风格  

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

首先,是三次握手


首先 Client 端发送连接请求报文,Server 端接受连接后回复 ACK 报文,并为这次连接分配资源。Client 端接收到 ACK 报文后也向 Server 段发生 ACK 报文,并分配资源,这样 TCP 连接就建立了

然后是中间部分: 两者之间可以传输数据了

再次,下面的断开链接:【注意】中断连接端可以是 Client 端,也可以是 Server 端

假设 Client 端发起中断连接请求,也就是发送 FIN 报文。Server 端接到 FIN 报文后,意思是说:“我 Client 端没有数据要发给你了”,但是如果你还有数据没有发送完成,则不必急着关闭 Socket,可以继续发送数据。

所以你先发送 ACK,“告诉 Client 端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候 Client 端就进入 FIN_WAIT 状态,继续等待 Server 端的 FIN 报文。当 Server 端确定数据已发送完成,则向 Client 端发送 FIN 报文,“告诉 Client端,好了,我这边数据发完了,准备好关闭连接了”。

Client 端收到 FIN 报文后,就知道可以关闭连接了,但是他还是不相信网络,怕 Server 端不知道要关闭,所以发送 ACK 后进入 TIME_WAIT 状态,如果 Server 端没有收到 ACK 则可以重传。Server 端收到 ACK 后,“就知道可以断开连接了”。Client 端等待了 2MSL 后依然没有收到回复,则证明 Server 端已正常关闭,那好,我 Client 端也可以关闭连接了。

OK,TCP连接就这样关闭了!

那么可以这么理解,当 Client 进入 TIME_WAIT 的等待时间是 2 个 MSLMaximum Segment Lifetime - 报文最大生存时间)。

让我们看一下一台 Linux 服务器的网络状态:

$ netstat -an | awk '/^tcp/ {++State[$NF]}END{for(key in State)print key "\t" State[key]}'LAST_ACK  7

LISTEN  9
SYN_RECV  2
CLOSE_WAIT  125
ESTABLISHED  1070
FIN_WAIT1  17
FIN_WAIT2  247
CLOSING  4
TIME_WAIT  25087

对于网站来说,这样的 TIME_WAIT 略显偏高, 也就是说大量的关闭操作在等待 2 个 MSL 后结束,正常我们的 TCP 端口是 65535 个,如果并发再高一些,可能会大量的 Socket 不能及时被释放,从而导致性能下降,所以我们可以通过 Linux 内核进行一些网络调整比如,开启 Socket 重用和快速回收,文件 /etc/sysctl.conf,生效命令为 sysctl -p

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 1024 65000

net.ipv4.tcp_syncookies = 1

表示开启 SYN Cookies。当出现 SYN 等待队列溢出时,启用 cookies 来处理,可防范少量 SYN 攻击,默认为 0,表示关闭;

net.ipv4.tcp_tw_reuse = 1

表示开启重用。允许将 TIME-WAIT Sockets 重新用于新的TCP连接,默认为 0,表示关闭;

net.ipv4.tcp_tw_recycle = 1

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

系统 tcp_timestamps 缺省就是开启的,所以当 tcp_tw_recycle 被开启后,实际上这种行为就被激活了.如果服务器身处 NAT 环境,安全起见,通常要禁止 tcp_tw_recycle,至于 TIME_WAIT 连接过多的问题,可以通过激活 tcp_tw_reuse 来缓解。

net.ipv4.tcp_max_tw_buckets = 5000

表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT 套接字将立刻被清除并打印警告信息。默认为 180000,改为 5000。对于 Apache、Nginx 等服务器,上几行的参数可以很好地减少 TIME_WAIT 套接字数量,但是对于 Squid,效果却不大。此项参数可以控制 TIME_WAIT 套接字的最大数量,避免 Squid 服务器被大量的 TIME_WAIT 套接字拖死。

net.ipv4.tcp_max_syn_backlog = 8192

表示 SYN 队列的长度,默认为 1024,加大队列长度为 8192,可以容纳更多等待连接的网络连接数。

net.ipv4.tcp_keepalive_time = 1200

表示当 keepalive 起用的时候,TCP 发送 keepalive 消息的频度。缺省是 2 小时,改为 20 分钟。

net.ipv4.ip_local_port_range = 1024-65000

表示用于向外连接的端口范围。缺省情况下很小:32768 到 61000,改为 1024 到 65000。

$ netstat -an | awk '/^tcp/ {++State[$NF]}END{for(key in State)print key "\t" State[key]}'

LAST_ACK  140
LISTEN  9
SYN_RECV  7
CLOSE_WAIT  2
ESTABLISHED 972
FIN_WAIT1 21
FIN_WAIT2 152
CLOSING 2
TIME_WAIT 682

配置文件 /etc/sysctl.conf 参考


net.ipv4.ip_forward = 1
fs.file-max=1000000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096 87380 4194304
net.ipv4.tcp_wmem = 4096 16384 4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_fin_timeout = 20
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_max_orphans = 3276800

vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time=120
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.all.arp_announce=2
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 40240
net.core.netdev_max_backlog = 40240
net.core.somaxconn = 40240
net.ipv4.tcp_synack_retries = 2
net.ipv4.conf.lo.arp_announce=2
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_tw_recycle = 0  <---- 不要开启tcp_tw_recycle,尤其是 NAT 下
net.ipv4.ip_local_port_range = 1024    65000

修改配置文件 /etc/sysctl.conf 后,执行命令生效:


sysctl -p

TIME_WAIT 扩展阅读:




Linux TCP 状态 TIME_WAIT 过多的处理评论