tcp time_wait问题
目录
目录
问题出现:
在元旦前夕,自己维护的一个服务突然在高峰时期收到大量报警,赶紧登上服务器看一下:
最开始的反应是memcache tcp read time out ,因为之前也出现过类似的警告所以开始尝试切换memcache,但是运维反馈已经切换了好几台还是不起作用。
又看到有mysql 连接不上报错,怀疑机房内网有问题,然后有开始切换机房加机器,当时问题还是没有得到解决,这下真晕了。。。。 完全排查不出什么问题(因为是高峰时期,tcp连接数自然很高,没有在意是这个问题,自己预估不够)
后来反应过来看到有好几个接口请求数增加,因为发版本的原因,是新版本才会请求这些接口,所以 我暂时屏蔽了这几个口接口问题得到遏制(幸好是用户感知不到的接口)。
排查:
这个问题围绕我好几天,我屏蔽的那几个接口,反复看了好几遍,而且又将其中的sql查询分析,并没有慢请求。。。 这下又彻底懵了,没有头绪。
说来也巧,高峰时期我用netstat -an
命令查看了一些,看到大量tcp time_wait 的连接,赶紧Google了下,tcp time_wait状态是在主动关闭连接的一方保持的一个状态(一般指客户端),
总共的连接数在近3w(ss -s可查),其中有2w多都是这种连接,而且都是mysql 连接(这里边服务器程序就相当于客户端去连接mysql服务器),到这里心里大致有底了,把数据库连接池加上就好了,Golang提供了两个函数可进行配置:
|
|
为什么会有TIME_WAIT状态?
源于tcp链接关闭中的四次挥手,是主动关闭链接的一方产生的状态: TCP状态转化图三次握手/四次挥手:
四次挥手的过程如下:
-
主动关闭连接的一方,调用close();协议层发送FIN包
-
被动关闭的一方收到FIN包后,协议层回复ACK;然后被动关闭的一方,进入CLOSE_WAIT状态,主动关闭的一方等待对方关闭,则进入FIN_WAIT_2状态;此时,主动关闭的一方 等待 被动关闭一方的应用程序,调用close操作
-
被动关闭的一方在完成所有数据发送后,调用close()操作;此时,协议层发送FIN包给主动关闭的一方,等待对方的ACK,被动关闭的一方进入LAST_ACK状态;
-
主动关闭的一方收到FIN包,协议层回复ACK;此时,主动关闭连接的一方,进入TIME_WAIT状态;而被动关闭的一方,进入CLOSED状态
-
等待2MSL时间,主动关闭的一方,结束TIME_WAIT,进入CLOSED状态
所以time_wait属于tcp正常的一个状态,是为了解决网络的丢包和网络不稳定锁存在的一个状态。
因为当前服务器并发相对较大,所以存在了大量的链接为关闭,如果只是几百的话,也不会影响服务器性能。
使用连接池保存长链接,可使得链接复用,不会出现大量的这种状态。
反思:
问题终于找到解决,但是回过头来想了一下,其实一开始就可以定位到的,只是很多问题集中在一起出现反而让自己不知所措,这个时候如果是敏感的人的话应该马上能够从tcp状态上看出问题,之后线上遇到问题,重要的是先解决(不管通过什么方式),之后再一步步排查。