一、写在前面

  相信很多人都遇到过服务器出现大量TIME_WAIT的情况,大多数的解决办法是sysctl修改如下参数

net.ipv4.tcp_tw_recycle = 1   #快速回收 TIME_WAIT

net.ipv4.tcp_tw_reuse = 1    #reuse TIME_WAIT

net.ipv4.tcp_timestamps = 1  #上述两项生效的前提是TCP连接两端都要启用TCP时间戳

过一会发现TIME_WAIT数量直线下降后,服务貌似也没出问题,ok问题解决!其实不然。想真正理解所谓“大量的TIME_WAIT的问题”,我们要先理解TIME_WAIT。


二、概念解析

  TIME_WAIT是有友好的,不是多余的,是主动关闭TCP连接的一方在调用socker的close操作后最终会进入TIME_WAIT状态。服务器在处理客户端请求的时候,如果你的程序设计为服务器主动关闭,那么你才有可能需要关注这个TIMEWAIT状态过多的问题。如果你的服务器设计为被动关闭,那么你首先要关注的是CLOSE_WAIT。

  换句话说:在一台负载均衡的服务器上(以Nginx为例),客户端向Nginx的请求,作为服务器来看属于被动连接;Nginx向Web服务器的请求属于主动连接。在讨论TIME_WAIT优化时,我们应该关注的是主动连接,即Nginx对Web服务器的连接。


三、TIME_WAIT作用

  1、防止前一个连接延迟的数据被后面复用的连接错误的接收;

  2、可靠的关闭TCP连接;

  关于TIME_WAIT作用的深入理解需要配合Socket连接五元组、RFC 793等概念,本文不重点讨论,感兴趣的童鞋请移步老男孩博客

 

四、TIME_WAIT过多后的性能影响

  内核里有保存所有连接的一个hash table,这个hash table既包含TIME_WAIT状态的连接,也包含其它状态的连接。主要用于有新的数据到来的时候,从这个hash table里快速找到这条连接。还有一个hash  table用来保存所有的bound ports,主要用于可以快速的找到一个可用的端口或者随机端口。

  因此,内核保存这些数据必然会占用一定的内存,同理每次找到一个随机端口需要遍历一遍bound ports,必然需要一些CPU时间! 

  TIME_WAIT很多,既占内存又耗CPU,但其实进一步研究,1万条TIME_WAIT连接,也就多消耗1M左右的内存,对于现在的服务器来说已经不算什么。至于CPU,也不至于因为1万多的hash item就担忧。最起码在TIME_WAIT达到几千的量级上不必过多紧张,因为TIME_WAIT所占用的内存很少很少,同时记录和寻找可用的local port所消耗的CPU也基本可以忽略。



五、打开net.ipv4.tcp_tw_recycle参数的副作用

  TIME_WAIT的存在是很重要的,如果强制忽略TIME_WAIT,还是有很高的机率,造成数据粗乱,或者短暂性的连接失败。比较直接的现象是,通过NAT后的IP大量访问服务的时候容易出现静置几分钟后连接失败或者多个客户端同时访问有的访问频繁失败的情况。

  还有一种情况是:在 客户端-服务器 一对一的时候,没有任何问题,但是当源IP是经过NAT后的地址或服务器在负载均衡器后面时,源地址为同一ip假如恰巧先后两次连接源端口相同,这台服务器先后收到两个包,第一个包的timestamp被服务器保存着,第二个包又来了,一对比,发现第二个包的timestamp比第一个还老——客户端时间不一致。服务器基于PAWS,判断第二个包是重复报文,丢弃之。反映出来的情况就是在服务器上抓包,发现有SYN包,但服务器就是不回ACK包,因为SYN包已经被丢弃了。可用如下命令验证,查看输出里面的packets rejects in established connections because of timestamp一项的数量变化。

netstat -s | grep timestamp


六、net.ipv4.tw_recycle如何配置

  写了这么多,那么tw_recycle参数到底该怎么用呢?在这里,引用老男孩博客里给出的两个典型场景的配置方案作为参考

  方案一:负载均衡服务器首先关闭连接 

  在这种情况下,因为负载均衡服务器对Web服务器的连接(注意,负载均衡器向Web服务器发起的主动连接),TIME_WAIT大都出现在负载均衡服务器上,所以,在负载均衡服务器上的配置:

  • net.ipv4.tcp_tw_reuse = 1 //尽量复用连接

  • net.ipv4.tcp_tw_recycle = 0 //不能保证客户端不在NAT的网络

    在Web服务器上的配置为:

  • net.ipv4.tcp_tw_reuse = 1 //这个配置主要影响的是Web服务器到DB服务器的连接复用

  • net.ipv4.tcp_tw_recycle:
    设置成1和0都没有任何意义。想一想,在负载均衡和它的连接中,它是服务端,但是TIME_WAIT出现在负载均衡服务器上;它和DB的连接,它是客户端,recycle对它并没有什么影响,关键是reuse

     

    方案二:Web服务器首先关闭来自负载均衡服务器的连接

    在这种情况下,Web服务器变成TIME_WAIT的重灾区。负载均衡对Web服务器的连接,由Web服务器首先关闭连接,TIME_WAIT出现在Web服务器上;Web服务器对DB服务器的连接,由Web服务器关闭连接,TIME_WAIT也出现在它身上,此时,负载均衡服务器上的配置:

  • net.ipv4.tcp_tw_reuse:0 或者 1 都行,都没有实际意义

  • net.ipv4.tcp_tw_recycle=0 //一定是关闭recycle

    在Web服务器上的配置:

  • net.ipv4.tcp_tw_reuse = 1 //这个配置主要影响的是Web服务器到DB服务器的连接复用

  • net.ipv4.tcp_tw_recycle=1 //由于在负载均衡和Web服务器之间并没有NAT的网络,可以考虑开启recycle,加速由于负载均衡和Web服务器之间的连接造成的大量TIME_WAIT


七、参考和引用资料

http://blog.oldboyedu.com/tcp-wait/

http://blog.chinaunix.net/uid-24517549-id-4048652.html

http://blog.chinaunix.net/uid-28337979-id-4107112.html 


    写在最后,内核的优化不能想当然,需要配合相应的业务场景。当然,这是一个长期积累的过程,需要不断的研究摸索,也需要众位同道多多分享多多赐教!与君共勉!