聊一聊TCP协议的TIME_WAIT与性能优化

Posted Qtest之道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊一聊TCP协议的TIME_WAIT与性能优化相关的知识,希望对你有一定的参考价值。

序言

我们做性能测试或者服务端调优时,经常会查看服务器的tcp连接情况, 偶尔会出现大量的TIME_WAIT状态,本篇文章将解释一下这个状态,并用实际性能测试例子模拟一下出现的场景与调优办法。

聊一聊TCP协议的TIME_WAIT与性能优化

01

TCP连接的状态

我们在学习计算网络的时候,都知道TCP的三次握手与四次挥手(面试经常问哈),四次挥手如下图所示:


聊一聊TCP协议的TIME_WAIT与性能优化

如图所示挥手过程即:

第一次挥手:主机A向主机B,发送FIN报文段,表示关闭数据传送,并主机A进入FIN_WAIT_1状态,表示没有数据要传输了 。
第二次挥手: 主机B收到FIN报文段后进入CLOSE_WAIT状态(被动关闭),然后发送ACK确认,表示同意你关闭请求了,主机A到主机B的数据链路关闭,主机A进入FIN_WAIT_2状态 
第三次挥手:主机B等待主机A发送完数据,发送FIN到主机A请求关闭,主机B进入LAST_ACK状态 
第四次挥手:主机B收到主机A发送的FIN后,回复ACK确认到主机B,主机A进入TIME_WAIT状态。主机B收到主机A的ACK后就关闭连接了,状态为CLOSED。主机A等待2MSL,仍然没有收到主机B的回复,说明主机B已经正常关闭了,主机A关闭连接。

在四次挥手过程中,会出现如下几种状态:

FIN_WAIT_1:表示等待对方的FIN报文。当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET进入到FIN_WAIT_1 状态 
FIN_WAIT_2:也表示等待对方的FIN报文。FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。 
CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。你回复一个ACK给对方,并进入CLOSE_WAIT状态。接下来就是查看你是否还有数据要发送给对方,如果没有,就可以close这个socket,并发送FIN给对方,即关闭连接。 
CLOSING:表示主机A给主机B发送FIN后,并没有收到主机B回应的ACK,而收到了主机B发送的FIN。表示双方同时close一个socket,出现同时发送FIN现象。 
LAST_ACK: 发送FIN报文后,等待对方的ACK报文,当收到ACK报文后,进入到CLOSED状态。 
TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK确认,等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态。


 大家看了上述挥手过程与状态描述,会感觉TIME_WAIT状态好像没什么作用。


聊一聊TCP协议的TIME_WAIT与性能优化

但其实并不是这样。

《计算机网络》第五版有如下解释:(划重点)

第一,为了保证A发送的最后一个ACK报文能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后就立即释放连接,就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常的步骤进入CLOSED状态。 
第二,A在发送完ACK报文段后,再经过2MSL时间,就可以使本连接持续的时间所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求的报文段。

注: MSL即报文最大生存时间。


02

大量TIME_WAIT状态

产生TIME_WAIT状态先决条件是TCP对话的双方中的一方主动关闭连接。即可以是客户端也可以是服务器端。而为了实现TCP全双工连接的正常终止,必须处理终止过程中四个分节任何一个分节的丢失情况,所以主动关闭连接的一方必须维持TIME_WAIT状态。这样就会导致,如果有大量连接主动关闭了,就会有大量的TIME_WAIT状态保存在服务器中

而我们现实中的业务,更多关注的是应用层协议,即基于tcp协议的HTTP协议。使用keep-alive可以改善大量的TIME_WAIT状态的产生。因为在一次TCP连接中可以持续发送多份数据而不会断开连接。在http1.1中,keep-alive是默认开启的,如果client不希望使用长连接,需要在header中设置connection:close。


03

 TIME_WAIT影响

对于请求方,比如一个服务需要通过http协议调用其他服务,或者压力测试的施压方。如果维护了大量的TIME_WAIT状态,就会造成没有足够的端口去发送请求,造成性能的下降或者达不到预期的服务效果。

为此我们对一个真实业务场景进行压测,通过采用connection:close与connection:keep-alive 做了2次压力测试,测试工具为大白性能测试平台

当采用 connection:keep-alive时,部分测试结果如下图所示:


聊一聊TCP协议的TIME_WAIT与性能优化

监控压力服务器的time_wait与建立连接数:


聊一聊TCP协议的TIME_WAIT与性能优化

发现time_wait连接数虽然随着压测的进行,一直在增长,但保持在系统可以工作的水平。


当采用connection:close时,查看部分测试结果如下:


聊一聊TCP协议的TIME_WAIT与性能优化

可以看到施压能力性能明显下降,在查看施压服务器的连接情况:


聊一聊TCP协议的TIME_WAIT与性能优化

可以发现time_wait成为了一条直线,且稳定再63000多,与系统最大端口数接近。

   注意,这里TPS上不去,并不是业务场景的问题,而是由于施压服务器即采用了短连接又没有优化服务器造成的,实际大白性能测试平台默认开启了keep-alive,并不会产生如上效果。


04

解决time_wait过多

那么如何解决服务器time_wati过多的情况呢?

(1)最简单的办法,就是将头部connection设置为keep-alive。现在的浏览器基本都采用http1.1服务。

(2)如果有特殊需求,并不想http设置为长连接,那么就需要修改内核参数,通过修改/etc/sysctl.conf。

通用的设置参数如下:

net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30

tcp_tw_recycle为可用于快速回收处于TIME_WAIT状态的socket以便重新分配,设置为1 即开启 ;

  tcp_tw_reuse设置为 1,即 开启该选项后,kernel会复用处于TIME_WAIT状态的socket,当然复用的前提是“从协议角度来看,复用是安全的”。

 由于本人对linux内核也不是十分了解,这些就不过多展开了~

 修改结束后sysctl –p 既可以使内核生效。

我们再通过了一次性能测试,来看看这么生效是否能达到预期效果。

(1)参数修改之前

服务器内核配置为

聊一聊TCP协议的TIME_WAIT与性能优化

施压服务器:

聊一聊TCP协议的TIME_WAIT与性能优化

随着压测的进去,time_wait一直在加大

开启配置后:

聊一聊TCP协议的TIME_WAIT与性能优化
聊一聊TCP协议的TIME_WAIT与性能优化

我们可以看到time_wait就不会有明显增加了,效果还是很立竿见影的。

但linux参数很复杂的,修改了tcp_tw_recycle与tcp_tw_reuse 也是有风险的,在这里就不展开了,实际业务开发与性能测试过程中,希望大家引起注意。


05

总结

本篇文章简单了总结了一下time_wait与出现大量过程后如何优化,希望大家在以后的性能测试过程中,注意查看服务器的一些 tcp状态,及时帮助业务的开发找到这些潜在影响系统性能的问题~


Qtest是360旗下的专业测试团队!

是WEB平台部测试技术平台化、效率化的先锋力量!


陪伴是最长情的告白

每日为你推送最in的测试技术


以上是关于聊一聊TCP协议的TIME_WAIT与性能优化的主要内容,如果未能解决你的问题,请参考以下文章

聊一聊数据库(MySQL)设计中的数据类型优化

TCP吞吐性能缺陷的根源

JVM性能提升50%,聊一聊背后的秘密武器Alibaba Dragonwell

JVM性能提升50%,聊一聊背后的秘密武器Alibaba Dragonwell

聊一聊并行文件系统的客户端优化之道

聊一聊浏览器事件循环与前端性能