环境 :Ubuntu 16.04 LTS
注意 :
TIMEWAIT状态本身和应用层的客户端或者服务器是没有关系的。如果你的程序设计为服务器主动关闭,那么你才有可能需要关注这个TIMEWAIT状态过多的问题。如果你的服务器设计为被动关闭,那么你首先要关注的是CLOSE_WAIT。
大量TIMEWAIT出现在业务上
在高并发短连接的TCP服务器上,当服务器处理完请求后立刻按照主动正常关闭连接。。。这个场景下,会出现大量socket处于TIMEWAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。
业务上两个方面需要注意:
1、高并发可以让服务器在短时间范围内同时占用大量端口,而端口有个0~65535的范围,并不是很多,刨除系统和其他服务要用的,剩下的就更少了。
2、在这个场景中,短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接。这里有个相对长短的概念,比如,取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIMEWAIT状态几分钟,而这几分钟,其他HTTP请求来临的时候是无法占用此端口的。单用这个业务计算服务器的利用率会发现,服务器干正经事的时间和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。(说个题外话,从这个意义出发来考虑服务器性能调优的话,长连接业务的服务就不需要考虑TIMEWAIT状态。同时,假如你对服务器业务场景非常熟悉,你会发现,在实际业务场景中,一般长连接对应的业务的并发量并不会很高)
综合这两个方面,持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分客户服务。同时,这些端口都是服务器临时分配,无法用SO_REUSEADDR选项解决这个问题:(
短时间后,所有的 TIME_WAIT 全都消失,被回收,端口包括服务,均正常。
Nginx 作为反向代理时,大量的短链接,可能导致 Nginx 上的 TCP 连接处于 time_wait 状态:
即,在高并发的场景下,TIME_WAIT 连接存在,属于正常现象。
线上场景中,持续的高并发场景
大量的 TIME_WAIT 状态 TCP 连接存在,其本质原因是什么?
1、大量的短连接存在
2、特别是 HTTP 请求中,如果 connection 头部取值被设置为 close 时,基本都由「服务端」发起主动关闭连接
3、而,TCP 四次挥手关闭连接机制中,为了保证 ACK 重发和丢弃延迟数据,设置 time_wait 为 2 倍的 MSL(报文最大存活时间)
TIME_WAIT 状态:
1、TCP 连接中,主动关闭连接的一方出现的状态;(收到 FIN 命令,进入 TIME_WAIT 状态,并返回 ACK 命令)
2、保持 2 个 MSL 时间,即,4 分钟;(MSL 为 2 分钟)
思路 :
1、可以修改内核协议栈代码中关于这个TIMEWAIT的超时时间参数,重编内核,让它缩短超时时间,加快回收;
2、利用SO_LINGER选项的强制关闭方式,发RST而不是FIN,来越过TIMEWAIT状态,直接进入CLOSED状态。
查看进程连接状态
统计 TCP 连接的状态:
统计:各种连接的数量
#netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c
16 CLOSING
130 ESTABLISHED
298 FIN_WAIT1
13 FIN_WAIT2
9 LAST_ACK
7 LISTEN
103 SYN_RECV
5204 TIME_WAIT
#netstat -nat |grep TIME_WAIT
或者
统计:各种连接的数量
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ESTABLISHED 1154
TIME_WAIT 1645
查询 TCP 连接状态,其中 -E 表示 grep 或的匹配逻辑
$ netstat -nat | grep -E "TIME_WAIT|Local Address"
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 127.0.0.1.1080 127.0.0.1.59061 TIME_WAIT
查询 TCP 连接状态
$ netstat -nat |grep TIME_WAIT
查询 TCP 连接状态,其中 -E 表示 grep 或的匹配逻辑
$ netstat -nat | grep -E "TIME_WAIT|Local Address"
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp4 0 0 127.0.0.1.1080 127.0.0.1.59061 TIME_WAIT
Tips:TCP 本地端口数量,上限为 65535(6.5w),这是因为 TCP 头部使用 16 bit,存储「端口号」,因此约束上限为 65535。
状态:描述
CLOSED: 无连接是活动的或正在进行
LISTEN: 服务器在等待进入呼叫
SYN_RECV: 一个连接请求已经到达,等待确认
SYN_SENT: 应用已经开始,打开一个连接
ESTABLISHED: 正常数据传输状态
FIN_WAIT1: 应用说它已经完成
FIN_WAIT2: 另一边已同意释放
ITMED_WAIT: 等待所有分组死掉
CLOSING: 两边同时尝试关闭
TIME_WAIT: 另一边已初始化一个释放
LAST_ACK: 等待所有分组死掉
如发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决,
vim /etc/sysctl.conf
netstat下time_wait状态的tcp连接:
1.这是一种处于连接完全关闭状态前的状态;
2.通常要等上4分钟(windows server)的时间才能完全关闭;
3.这种状态下的tcp连接占用句柄与端口等资源,服务器也要为维护这些连接状态消耗资源;
4.解决这种time_wait的tcp连接只有让服务器能够快速回收和重用那些TIME_WAIT的资源:修改注册表[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\Parameters]添加dword值TcpTimedWaitDelay=30(30秒也为微软建议值;默认为2分钟)和MaxUserPort:65534(可选值5000 - 65534);
启动程序发现端口被占用,netstat查看之后发现如下现象:
发现端口处于TIME_WAIT状态以及FIN_WAIT2状态,无法释放
大量的 TIME_WAIT 状态 TCP 连接存在,其本质原因是什么?
1、大量的短连接存在
2、特别是 HTTP 请求中,如果 connection 头部取值被设置为 close 时,基本都由「服务端」发起主动关闭连接
3、而,TCP 四次挥手关闭连接机制中,为了保证 ACK 重发和丢弃延迟数据,设置 time_wait 为 2 倍的 MSL(报文最大存活时间)
TIME_WAIT 状态:
1、TCP 连接中,主动关闭连接的一方出现的状态;(收到 FIN 命令,进入 TIME_WAIT 状态,并返回 ACK 命令)
2、保持 2 个 MSL 时间,即,4 分钟;(MSL 为 2 分钟)
TIME_WAIT状态的来源 : 可能是之前虚拟机异常关机导致
TIME_WAIT状态 产生原因: 只有首先调用close()发起主动关闭的一方才会进入TIME_WAIT状态,进入TIME_WAIT状态的TCP连接需要经过2MSL才能回到初始状态,
避免办法 :尽量由客户端主动关闭,避免服务端出现time_wait
根据一个查询TCP连接数
netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}'
或者
netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c
解决上述 time_wait 状态大量存在,导致新连接创建失败的问题,一般解决办法:
1、客户端, HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间:现在的浏览器,一般都这么进行了
2、服务器端,
允许 time_wait 状态的 socket 被重用
缩减 time_wait 时间,设置为 1 MSL(即,2 mins)
3、编辑内核文件/etc/sysctl.conf,加入以下内容:
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,表示关闭。
net.ipv4.tcp_fin_timeout = 30 修改系默认的 TIMEOUT 时间
然后执行 /sbin/sysctl -p 让参数生效.
/etc/sysctl.conf是一个允许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。
注释 :
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,表示关闭。
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间
简单来说,就是打开系统的TIMEWAIT重用和快速回收。
如果以上配置调优后性能还不理想,可继续修改一下配置:
vi /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 1200
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.ip_local_port_range = 1024 65000
#表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
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_syncookies=1 打开TIME-WAIT套接字重用功能,对于存在大量连接的Web服务器非常有效。
net.ipv4.tcp_tw_recyle=1
net.ipv4.tcp_tw_reuse=1 减少处于FIN-WAIT-2连接状态的时间,使系统可以处理更多的连接。
net.ipv4.tcp_fin_timeout=30 减少TCP KeepAlive连接侦测的时间,使系统可以处理更多的连接。
net.ipv4.tcp_keepalive_time=1800 增加TCP SYN队列长度,使系统可以处理更多的并发连接。
net.ipv4.tcp_max_syn_backlog=8192
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
FIN_WAIT_2状态很多的原因: 服务端关闭,但客户端没有关闭。
解决:
1.服务端程序读到 -1时,应该关闭连接,这样服务端就会发起fin 指令。
进程关闭也会自动发fin 指令。
2. tcp协议里没有针对这个状态设置超时时间,但linux服务器实现了超时回收,
tcp_fin_timeout ,默认是60秒, 可通过 /sbin/sysctl -a | grep timeout 查看
网络超时:socket本身有 重连控制,网络拥塞控制。
1、time_wait 状态的影响:
TCP 连接中,「主动发起关闭连接」 的一端,会进入 time_wait 状态
time_wait 状态,默认会持续 2 MSL(报文的最大生存时间),一般是 2x2 mins
time_wait 状态下,TCP 连接占用的端口,无法被再次使用
TCP 端口数量,上限是 6.5w(65535,16 bit)
大量 time_wait 状态存在,会导致新建 TCP 连接会出错,address already in use : connect 异常
2、现实场景:
1、服务器端,一般设置:不允许「主动关闭连接」
2、但 HTTP 请求中,http 头部 connection 参数,可能设置为 close,则,服务端处理完请求会主动关闭 TCP 连接
3、现在浏览器中, HTTP 请求 connection 参数,一般都设置为 keep-alive
4、Nginx 反向代理场景中,可能出现大量短链接,服务器端,可能存在
3、解决办法:
服务器端,
允许 time_wait 状态的 socket 被重用
缩减 time_wait 时间,设置为 1 MSL(即,2 mins)
FIN_WAIT1:
1、sysctl -a |grep tcp_max_orph(记下 net.ipv4.tcp_max_orphans 的值 第三步需要赋给orig_orphans)
2、sysctl -w net.ipv4.tcp_max_orphans=0 然后等待FIN_WAIT1的消失,可以用 netstat -np|grep 9080 反复查看,直到没有任何条目
3、sysctl -w net.ipv4.tcp_max_orphans=$orig_orphans
解决TIME_WAIT过多造成的问题 : https://www.cnblogs.com/dadonggg/p/8778318.html
netstat -an查看到大量的TIME_WAIT状态的解决办法 :https://www.cnblogs.com/mobilecard/archive/2018/08/13/9468934.html
FIN_WAIT_2 tcp状态多原因剖析和解决 :https://blog.csdn.net/fei33423/article/details/50889385
基础原理系列:服务端 TCP 连接的 TIME_WAIT 问题 :https://ningg.top/computer-basic-theory-tcp-time-wait/
一文聊聊服务端 TCP 连接的 TIME_WAIT :https://mp.weixin.qq.com/s/94S1zOpOZTsNSNU2Y-ihdw
更多介绍 :
https://blog.csdn.net/Lange_Taylor/article/details/100098911