为了能彻底讲清楚TIME_WAIT的原理及解决办法,本系列一共有4篇
彻底理解并解决服务器出现大量TIME_WAIT - 第一篇_YZF_Kevin的博客
彻底理解并解决服务器出现大量TIME_WAIT - 第二篇_YZF_Kevin的博客
彻底理解并解决服务器出现大量TIME_WAIT - 第三篇_YZF_Kevin的博客
彻底理解并解决服务器出现大量TIME_WAIT - 第四篇_YZF_Kevin的博客-
第一篇博客中我们讲了 TIME_WAIT 出现的原理,引发的问题,解决办法等,如下
解决办法
1. 代码层修改,把短连接改为长连接,但代价较大
2. 修改 ip_local_port_range,增大可用端口范围,比如1024 ~ 65535
3. 客户端程序中设置socket的 SO_LINGER 选项
4. 打开 tcp_tw_recycle 和tcp_timestamps 选项,有一定风险,且linux4.12之后被废弃
5. 打开 tcp_tw_reuse 和 tcp_timestamps 选项
6. 设置 tcp_max_tw_buckets 为一个较小的值
下面我们开始对各个办法进行详细讲解
办法1:代码层修改,把短连接改为长连接
由于TIME_WAIT出现的根本原因是高并发且持续的短连接,所以如果能把短连接改成长连接,就能彻底解决问题。比如http请求中的connection设置为keep-alive。只是代码层的修改往往会比较大,不再啰嗦
办法2. 修改 ip_local_port_range,增大可用端口范围
我这里linux系统是ubuntu,输入如下命令可查看可用的端口范围
默认差不多有3万个,我们可以修改这个值,但是注意最小不能小于1024,最大不能大于65535,也就是说改完之后最多有6万多个可用端口
只是一般线上遇到大量的TIME_WAIT,都是高并发且持续的短连接,单纯扩大端口范围并不能从根本上解决问题,只是能多撑一会儿
办法3. 客户端程序中设置socket的 SO_LINGER 选项
SO_LINGER 选项可以用来控制调用close函数关闭连接后的行为,linger的定义如下
struct linger {
int l_onoff; /* 0 = off, nozero = on */
int l_linger; /* linger time */
};
有三种情况
1. 设置 l_onoff 为0,l_linger的值会被忽略,也是内核缺省的情况,和不设置没区别。close调用会立即返回给调用者,TCP模块负责尝试发送残留的缓冲区数据,会经过通常四分组终止序列(FIN/ACK/FIN/ACK),不能解决任何问题
2. 设置 l_onoff 为1,l_linger为0,则连接立即终止,TCP将丢弃残留在发送缓冲区中的任何数据并发送一个RST报文给对方,而不是通常的四分组终止序列。对方收到RST报文后直接进入CLOSED状态,从根本上避免了TIME_WAIT状态
3. 设置 l_onoff 为1,l_linger > 0,有两种情况
a. 如果socket为阻塞的,则close将阻塞等待l_linger 秒的时间。如果在l_linger秒时间内TCP模块成功发送完残留在缓冲区的数据,则close返回0,表示成功。如果l_linger时间内TCP模块没有成功发送残留的缓冲区数据,则close返回-1,表示失败,并将errno设置为EWOULDBLOCK
b. 如果socket为非阻塞的,那么close立即返回,此时需要根据close返回值以及errno来判断TCP模块是否成功发送残留在缓冲区的数据
第3中情况,其实就是第1种和第2种的折中处理,且当socket为非阻塞的场景下是没有作用的
综上所述:第2种情况,也就是l_onoff为1,l_linger不为0,可以用于解决服务器大量TIME_WAIT的问题
只是Linux上测试的时候,并未发现发送了RST报文,而是正常进行了四步关闭流程,
初步推断是“只有在丢弃数据的时候才发送RST”,如果没有丢弃数据,则走正常的关闭流程
查看Linux源码,确实有这么一段注释和源码
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
*/
if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk->sk_allocation);
}
从原理上来说,这个选项有一定的危险性,可能导致丢数据,使用的时候要小心一些
从实测情况来看,打开这个选项后,服务器TIME_WAIT连接数为0,且不受网络组网(例如是否虚拟机等)的影响
标签:linger,tcp,TCP,TIME,close,第二篇,WAIT From: https://blog.51cto.com/u_15912066/5936276