好好爱自己!

【转】ubuntu中解决进程的time_wait和FIN_WAIT2状态

 

原文:https://blog.csdn.net/wangshuminjava/article/details/107386053

 

https://ningg.top/computer-basic-theory-tcp-time-wait/

 

 

 

---------------------------------------

 

TIMEWAIT状态本身和应用层的客户端或者服务器是没有关系的。如果你的程序设计为服务器主动关闭,那么你才有可能需要关注这个TIMEWAIT状态过多的问题。如果你的服务器设计为被动关闭,那么你首先要关注的是CLOSE_WAIT。
1
大量TIMEWAIT出现在业务上

在高并发短连接的TCP服务器上,当服务器处理完请求后立刻按照主动正常关闭连接。。。这个场景下,会出现大量socket处于TIMEWAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。
1
业务上两个方面需要注意:

1、高并发可以让服务器在短时间范围内同时占用大量端口,而端口有个0~65535的范围,并不是很多,刨除系统和其他服务要用的,剩下的就更少了。

2、在这个场景中,短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接。这里有个相对长短的概念,比如,取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIMEWAIT状态几分钟,而这几分钟,其他HTTP请求来临的时候是无法占用此端口的。单用这个业务计算服务器的利用率会发现,服务器干正经事的时间和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。(说个题外话,从这个意义出发来考虑服务器性能调优的话,长连接业务的服务就不需要考虑TIMEWAIT状态。同时,假如你对服务器业务场景非常熟悉,你会发现,在实际业务场景中,一般长连接对应的业务的并发量并不会很高)
1
2
3
综合这两个方面,持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分客户服务。同时,这些端口都是服务器临时分配,无法用SO_REUSEADDR选项解决这个问题:(

解决进程的time_wait状态

短时间后,所有的 TIME_WAIT 全都消失,被回收,端口包括服务,均正常。
Nginx 作为反向代理时,大量的短链接,可能导致 Nginx 上的 TCP 连接处于 time_wait 状态:

1、每一个 time_wait 状态,都会占用一个「本地端口」,上限为 65535(16 bit,2 Byte);
2、当大量的连接处于 time_wait 时,新建立 TCP 连接会出错,address already in use : connect
异常
即,在高并发的场景下,TIME_WAIT 连接存在,属于正常现象。
线上场景中,持续的高并发场景

一部分 TIME_WAIT 连接被回收,但新的 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状态。
1
2
查看进程连接状态
统计 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
状态:描述

CLOSED: 无连接是活动的或正在进行
LISTEN: 服务器在等待进入呼叫
SYN_RECV: 一个连接请求已经到达,等待确认
SYN_SENT: 应用已经开始,打开一个连接
ESTABLISHED: 正常数据传输状态
FIN_WAIT1: 应用说它已经完成
FIN_WAIT2: 另一边已同意释放
ITMED_WAIT: 等待所有分组死掉
CLOSING: 两边同时尝试关闭
TIME_WAIT: 另一边已初始化一个释放
LAST_ACK: 等待所有分组死掉
1
2
3
4
5
6
7
8
9
10
11
如发现系统存在大量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);
1
2
3
4
启动程序发现端口被占用,netstat查看之后发现如下现象:

发现端口处于TIME_WAIT状态以及FIN_WAIT2状态,无法释放

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
1
2
3


解决办法
解决上述 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 时间
1
2
3
4
然后执行 /sbin/sysctl -p 让参数生效.

/etc/sysctl.conf是一个允许改变正在运行中的Linux系统的接口,它包含一些TCP/IP堆栈和虚拟内存系统的高级选项,修改内核参数永久生效。
1
注释 :

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 时间
1
2
3
4
简单来说,就是打开系统的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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


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
1
2
3
4
5
查看效果


解决进程的FIN_WAIT2状态
FIN_WAIT_2状态很多的原因: 服务端关闭,但客户端没有关闭。
解决:

1.服务端程序读到 -1时,应该关闭连接,这样服务端就会发起fin 指令。
进程关闭也会自动发fin 指令。

2. tcp协议里没有针对这个状态设置超时时间,但linux服务器实现了超时回收,
1
2
3
4
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

linux端口被占用,netstat查看无进程号,端口状态一直停留在FIN_WAIT1以及CLOSE_WAIT状态
https://blog.csdn.net/microgp/article/details/86588973
http://blog.sina.com.cn/s/blog_77d329940102xa4t.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/
————————————————
版权声明:本文为CSDN博主「王树民」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangshuminjava/article/details/107386053

posted @ 2023-02-10 11:36  立志做一个好的程序员  阅读(508)  评论(0编辑  收藏  举报

不断学习创作,与自己快乐相处