首页 > 其他分享 >彻底理解并解决服务器出现大量TIME_WAIT - 第三篇

彻底理解并解决服务器出现大量TIME_WAIT - 第三篇

时间:2022-12-14 11:33:18浏览次数:94  
标签:第三篇 tw 虚拟机 ts tcp TIME recycle timestamps WAIT


第一篇博客中我们讲了 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 为一个较小的值
 

下面我们接着对各个办法进行详细讲解

办法4. 打开 tcp_tw_recycle 和tcp_timestamps 选项,有一定风险,且linux4.12之后被废弃

官方文档中解释如下:
tcp_tw_recycle 选项作用为:Enable fast recycling TIME-WAIT sockets. Default value is 0.
tcp_timestamps 选项作用为:Enable timestamps as defined in RFC1323. Default value is 1

这两个选项是linux内核提供的控制选项,和具体的应用程序没有关系,而且网上也能够查询到大量的相关资料,但信息都不够完整,最主要的几个问题如下;
1)快速回收到底有多快?
2)有的资料说只要打开tcp_tw_recycle即可,有的又说要tcp_timestamps同时打开,到底是哪个正确?
3)为什么从虚拟机NAT出去发起客户端连接时选项无效,非虚拟机连接就有效?为了搞清楚上面的疑问,只能看代码,看出一些相关的代码供大家参考:
=====linux-2.6.37 net/ipv4/tcp_minisocks.c 269======

void tcp_time_wait(struct sock *sk, int state, int timeo)
{
struct inet_timewait_sock *tw = NULL;
const struct inet_connection_sock *icsk = inet_csk(sk);
const struct tcp_sock *tp = tcp_sk(sk);
int recycle_ok = 0;


// 判断是否快速回收,这里可以看出tcp_tw_recycle和tcp_timestamps两个选项都打开的时候才进行快速回收,
//且还有进一步的判断条件,后面会分析,这个进一步的判断条件和第三个问题有关
if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);


if (tcp_death_row.tw_count < tcp_death_row.sysctl_max_tw_buckets)
tw = inet_twsk_alloc(sk, state);


if (tw != NULL) {
struct tcp_timewait_sock *tcptw = tcp_twsk((struct sock *)tw);
//计算快速回收的时间,等于 RTO * 3.5,回答第一个问题的关键是RTO(RetransmissionTimeout)大概是多少
const int rto = (icsk->icsk_rto << 2) - (icsk->icsk_rto >> 1);

//。。。。。。此处省略很多代码。。。。。。

if (recycle_ok)
{
//设置快速回收的时间
tw->tw_timeout = rto;
}
else
{
tw->tw_timeout = TCP_TIMEWAIT_LEN;
if (state == TCP_TIME_WAIT)
timeo = TCP_TIMEWAIT_LEN;
}

//。。。。。。此处省略很多代码。。。。。。
}

这里讲下RTO(​​Retransmission Time Out​​​):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传
RFC中有关于RTO计算的详细规定,一共有三个:RFC-793、RFC-2988、RFC-6298,Linux的实现是参考RFC-2988。
对于这些算法的规定和Linux的实现,有兴趣的同学可以自己深入研究,实际应用中我们只要记住Linux如下两个边界值:
=====linux-2.6.37 net/ipv4/tcp.c 126================
#define TCP_RTO_MAX ((unsigned)(120*HZ))
#define TCP_RTO_MIN ((unsigned)(HZ/5))
==========================================
这里的HZ是1s,因此可以得出RTO最大是120s,最小是200ms,对于局域网的机器来说,正常情况下RTO基本上就是200ms,因此3.5 RTO就是700ms
也就是说,快速回收是TIME_WAIT的状态持续700ms,而不是正常的2MSL
实测结果也验证了这个推论,不停的查看TIME_WAIT状态的连接,偶尔能看到1个

最后一个问题是为什么从虚拟机发起的连接即使设置了tcp_tw_recycle和tcp_timestamps,也不会快速回收,继续看代码:
tcp_time_wait函数中的代码行:recycle_ok = icsk->icsk_af_ops->remember_stamp(sk);对应的实现如下:
=====linux-2.6.37 net/ipv4/tcp_ipv4.c 1772=====

int tcp_v4_remember_stamp(struct sock *sk)
{
//。。。。。。此处省略很多代码。。。。。。

//当获取对端信息时,进行快速回收,否则不进行快速回收
if (peer)
{
if ((s32)(peer->tcp_ts - tp->rx_opt.ts_recent) <= 0 ||
((u32)get_seconds() - peer->tcp_ts_stamp > TCP_PAWS_MSL &&
peer->tcp_ts_stamp <= (u32)tp->rx_opt.ts_recent_stamp))
{
peer->tcp_ts_stamp = (u32)tp->rx_opt.ts_recent_stamp;
peer->tcp_ts = tp->rx_opt.ts_recent;
}

if (release_it)
inet_putpeer(peer);
return 1;
}

return 0;
}

上面这段代码应该就是测试的时候虚拟机环境不会释放的原因,当使用虚拟机NAT出去的时候,服务器无法获取隐藏在NAT后的机器信息。
生产环境也出现了设置了选项,但TIME_WAIT连接数达到4W多的现象,可能和虚拟机有关,也可能和组网有关。
总结一下:
1)快速回收到底有多快?
答:局域网环境下,700ms就回收
2)有的资料说只要打开tcp_tw_recycle即可,有的又说要tcp_timestamps同时打开,到底是哪个正确?
答:需要同时打开,但默认情况下tcp_timestamps就是打开的,所以会有人说只要打开tcp_tw_recycle即可
3)为什么从虚拟机发起客户端连接时选项无效,非虚拟机连接就有效?
答:和网络组网有关系,无法获取对端信息时就不进行快速回收。

注意1

NAT(Network Address Translation即网络地址转换)环境下,打开 tcp_tw_recycle选项可能会引发其他问题,tcp_tw_recycle是依赖tcp_timestamps参数的。例如办公室的外网地址只有一个,所有人访问后台都会通过路由器做SNAT将内网地址映射为公网IP,由于服务端和客户端都启用了tcp_timestamps,因此TCP头部中增加时间戳信息,而在服务器看来,同一客户端的时间戳必然是线性增长的,但是,由于我的客户端网络环境是NAT,因此每台主机的时间戳都是有差异的,在启用tcp_tw_recycle后,一旦有客户端断开连接,服务器可能就会丢弃那些时间戳较小的客户端的SYN包,这也就导致了网站访问极不稳定。

简单来说就是,Linux会丢弃所有来自远端的timestramp时间戳小于上次记录的时间戳(由同一个远端发送的)的任何数据包。也就是说要使用该选项,则必须保证数据包的时间戳是单调递增的。同时从4.10内核开始,官方修改了时间戳的生成机制,所以导致 tcp_tw_recycle 和新时间戳机制工作在一起不那么友好,同时 tcp_tw_recycle 帮助也不那么的大。

此处的时间戳并不是我们通常意义上面的绝对时间,而是一个相对时间。很多情况下,我们是没法保证时间戳单调递增的,比如业务服务器之前部署了NAT,LVS等情况。相信很多小伙伴上班的公司大概率实用实用各种公有云,而各种公有云的 LVS 网关都是 FullNAT 。所以可能导致在高并发的情况下,莫名其妙的 TCP 建联不是那么顺畅或者丢连接

主机A SIP:P1 (时间戳T0) —> Server

主机A断开后
主机B SIP:P1 (时间戳T1) T1 < T0 —> Server 丢弃

具体见:​​记一次由tcp_tw_recycle参数引发的血案_鲁班6号的技术博客_51CTO博客​

注意2

在linux内核版本从4.12之后,tcp_tw_recycle已经被废弃了,详见

​Troubleshooting sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directory​

我的linux机器是ubuntu,查看内核版本如下

彻底理解并解决服务器出现大量TIME_WAIT - 第三篇_TIME_WAIT解决

linux 内核版本已经4.15了,如果开发tcp_tw_recycle,执行命令刷新的话,就会有如下报错

彻底理解并解决服务器出现大量TIME_WAIT - 第三篇_服务器_02

 提示得很明确,这个文件不存在

sysctl: cannot stat /proc/sys/net/ipv4/tcp_tw_recycle: No such file or directory

综上

可以看出这种方法不是很保险,在实际应用中可能受到虚拟机、网络组网、防火墙之类的影响从而导致不能进行快速回收。

标签:第三篇,tw,虚拟机,ts,tcp,TIME,recycle,timestamps,WAIT
From: https://blog.51cto.com/u_15912066/5936288

相关文章