TCP time_wait close_wait问题(多是全网最清楚的例子)

背景

公司群里,运维发现一个问题,task服务报错(以下)python

The stream or file \"/data/logs/adn_task/offer_service.log\" could not be opened:
failed to open stream: Too many open files

测试老大看到了,根据经验就推测是应该是文件句柄使用完了,应该有TCP链接不少没释放,果然发现是不少CLOSE_WAIT的状态linux

简单认知

短连接,一次连接就会占用一个端口,一个端口就是一个文件描述符;
文件描述符 又称 句柄,linux系统最大的句柄数是65535,能够经过ulimit -a 查看
image.pngredis

三次握手

TCP创建链接须要通过三次握手;
通俗版本:
A: 你好,你能听见我说话吗?
B: 能听到,你能听到我说话吗?
A:我也能听到,咱们开始通讯吧shell

专业版本:
创建TCP链接时,须要客户端和服务器共发送3个包。服务器

  • 第一次:客户端发送初始序号x和syn=1请求标志
  • 第二次:服务器发送请求标志syn,发送确认标志ACK,发送本身的序号seq=y,发送客户端的确认序号ack=x+1
  • 第三次:客户端发送ACK确认号,发送本身的序号seq=x+1,发送对方的确认号ack=y+1

四次挥手

TCP链接断开须要通过四次挥手;
通俗版本:
前提A和B在通话
A:好的,个人话就说完了(FIN);
B:哦哦,我知道你说完啦(ACK),我还有说两句哈;A: (没说话,一直听着)
B:哦了,我也说完了(FIN)
A:好的,我也知道你说玩了(ACK),挂电话吧网络

专业版本:运维

  • 第一次挥手:客户端发出释放FIN=1,本身序列号seq=u,进入FIN-WAIT-1状态
  • 第二次挥手:服务器收到客户端的后,发出ACK=1确认标志和客户端的确认号ack=u+1,本身的序列号seq=v,进入CLOSE-WAIT状态
  • 第三次挥手:客户端收到服务器确认结果后,进入FIN-WAIT-2状态。此时服务器发送释放FIN=1信号,确认标志ACK=1,确认序号ack=u+1,本身序号seq=w,服务器进入LAST-ACK(最后确认态)
  • 第四次挥手:客户端收到回复后,发送确认ACK=1,ack=w+1,本身的seq=u+1,客户端进入TIME-WAIT(时间等待)。客户端通过2个最长报文段寿命后,客户端CLOSE;服务器收到确认后,马上进入CLOSE状态。

状态流转图

实际例子

创建链接

linux上起了一个redis服务
image.png
本地起的6379端口tcp

仍是同一台机器上,经过python脚本链接该redis服务:
image.png测试

此时网络链接以下:
image.png
关注这两个网络链接,第一个是redis-server的,第二是python脚本的,此时都是ESTABLISHED状态,表示这两个进程创建了链接优化

TIME_WAIT状况

如今断掉python
image.png
以前的python的那个链接,是TIME_WAIT状态
客户端(主动方)主动断开,进入TIME_WAIT状态,服务端(被动方)进去CLOSE状态,就是没有显示了

等待2MSL(1分钟)后,以下:
image.png
TIME_WAIT状态的链接也消失了,TIME_WAIT回收机制,系统ing过一段时间会回收,资源重利用

CLOSE_WAIT状况

先创建链接,以下:
image.png

关掉redis服务,service redis stop
image.png
以前的redis-server的45370端口链接 进入了FIN_WAIT2状态,而python端(被动关闭方)就进去了CLOSE_WAIT状态

等待30s后,在看链接
image.png
只有python的那条CLOSE_WAIT

再次操做python端的脚本,再次get
image.png

关于6379端口(redis端口)的网络链接都没有了
image.png

TCP参数设置

如何快速回收TIME_WAIT和FIN_WAIT
/etc/sysctl.conf 包含如下配置项
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
root权限 执行/sbin/sysctl -p使之生效

经验之谈

我的经验,不必定对,若有错误,请指正

  1. 当出现了CLOSE_WAIT大几率是业务代码问题,代码中没有处理服务异常的状况,如上面的例子,python再次请求redis的时候,发现redis挂了,就会主动干掉CLOSE_WAIT状态
  2. 出现大量TIME_WAIT的状况,通常是服务端没有及时回收端口,linux内核参数须要调整优化

参考资料

https://www.mobibrw.com/2019/20477

相关文章
相关标签/搜索