应对高负载Linux服务中 TCP TIME-WAIT问题

https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux#fn-netfilter

一句话总结| 不要开启 net.ipv4.tcp_tw_recycle

在Linux内核文档上,关于 net.ipv4.tcp_tw_recyclenet.ipv4.tcp_tw_reuse 都语焉不详。

针对recycle:能够快速回收TIME-WAIT状态的sockets,默认不开启。没有专家建议不应改动

针对reuse:能够复用处于TIME-WAIT状态的sockets,默认不开启。没有专家建议不应改动

官方文档的缺失导致了市面上很多调优指南上都建议开启这两个参数,以减少TIME-WAIT状态的链接。但是在 tcp(7) manual page上却提到,这两个参数的开启造成了一个十分难以发现的隐患:在NAT设备后的多个服务器可能会无法处理正常请求。

接下来我要给那些瞎逼逼的人上一课🐶

duty_calls

另外虽然从参数的的名称上有ipv4的字样,但是该参数同样适用于ipv6。所以谨记我们是在处理Linux tcp协议族的事情,如果从抓包的角度分析那就可能需要调整一下了。

TIME-WAIT 状态

让我们简单复习一下TIME-WAIT状态。它到底是什么?我们看一下TCP状态流转图:

tcp-state-diagram-v2.png

只有主动关闭链接的一方会进入到TIME-WAIT的状态,被动关闭的一方通常都会快速关闭。

目的

TIME-WAIT状态存在有两个目的:

  • 最被大家熟知的是出于可能存在数据包发送后有延迟,然后被后来创建的一个连接处理了。当然序列号( sequence number)也是在特定范围的才能接收,这让问题发生的概率降低了,但是问题仍然存在。RFC 1337解释了当TIME-WAIT不存在的时候的情况。以下是一个图解
duplicate-segment.png

但是这样的情况TIME-WAIT只保持一个MSL就可以了

  • 另外一个目的是确保远端连接已经关闭。如果最后一个ACK丢失,此时被动关闭端处于LAST-ACK状态,如果不存在time-wait 状态的话,一个创建新链接的请求可能会发送到LAST-ACK状态的连接上,此时端上会返回RST,新链接的创建就失败了。
last-ack.png

RFC 793 要求time-wait状态必要持续2 MSL,Linux上是不可调的,内核写死60s。
https://tools.ietf.org/html/rfc1185
The proper time to delay the final close step is not really
related to the MSL; it depends instead upon the RTO for the
FIN segments and therefore upon the RTT of the path.*
Although there is no formal upper-bound on RTT, common
network engineering practice makes an RTT greater than 1
minute very unlikely. Thus, the 4 minute delay in TIME-WAIT
state works satisfactorily to provide a reliable full-duplex
TCP close. Note again that this is independent of MSL
enforcement and network speed.

有人建议把这个参数设置成可调节的,但是基于保持time-wait状态是有益的这一前提下,该提议被否决了。

问题

为什么这个状态在处理海量连接的服务器上会变的十分恼人呢。以下是其中的三个问题:

无法创建新链接

socket 占用内存

额外的CPU使用

连接表卡槽

一个处于time-wait状态的连接大概会在连接表中存在一分钟,在这个时间内具有相同源地址源端口目标地址目标端口的连接是不能创建的。

对于一个web服务器来说,外网地址和端口往往都是固定的。如果是一个7层负载均衡后的web服务。源地址同样是固定的。在linux上客户端的端口范围大概是30000个(可以通过 net.ipv4.ip_local_port_range 参数更改),也就是说负载服务器和web服务器每分钟只能建立30000个,QPS大概是500

客户端的time-wait现象很容易定位,connect()的方法调用会抛出EADDRNOTAVAIL的异常,并且也会有对应的日志记录。但是在服务端就变得十分复杂了,只能通过命令行的方式看当前那些ip 端口被占用了。

  • 如果是客户端端口不够用,通过 net.ipv4.ip_local_port_range 调大端口的可用范围

  • 如果是服务端的端口不够用,添加额外的端口

  • 如果是客户端的IP不够用, 通过round-robin 进行客户端的负载均衡

  • 如果是服务端的ip不够用,配置多个服务端ip

内存

如果需要处理超多的连接,这些socket额外处于open状态一分钟会耗点内存,举个例子来说,如果每秒钟要处理10000个请求的话,time-wait状态的请求就会有60*10000个。那需要消耗的内存大概是多少呢?实际上也没有那么多

首先,从应用的角度考虑,一个处于time-wait状态的socket不会消耗任何的内存。因为socket已经被关闭了。从内核的角度考虑,socket主要以三种不同的形式存在内存中。

hash table of connections set of lists of connections hash table of bound ports

参考linux的代码,40000个入站连接大概需要10m的内存,40000个出站的连接大概需要2.5M。

CUP

从CPU的角度看,找到一个可用的端口确实是略昂贵的。 inet_csk_get_port() function 方法需要加锁迭代的方式找到可用的端口。 如果在TIME-WAIT状态下有很多出站连接(如与memcached服务器的短暂连接),则此哈希表中的大量条目通常不会成为问题:连接通常共享相同的配置文件,该功能将很快 找到一个可用端口,因为它按顺序迭代它们。

其他的解决办法

如果上面的回答仍然没有解决你的问题,还有三个方法可以尝试。

  • disable socket lingering,

  • net.ipv4.tcp_tw_reuse, and

  • net.ipv4.tcp_tw_recycle.

Socket lingering

当close() 被调用的时候,内核缓冲区的数据会被在后台继续发送然后socket最终会转为time-wait状态。应用程序可以立即继续工作,并假设最终将安全地传递所有数据。

应用程序可以选择禁用这个功能,称之为Socket lingering。它有以下两种特性:

1.余下的数据会被直接抛弃,而且不进行四次挥手的操作,连接直接发送RST并且直接关闭。不存在TINE-WAIt状态的情况。

2.第二个场景下,如果发送缓存中仍然有未发送的数据,进程在调用close()后会休眠直到数据完全发完并且已经通知到了对端,或者配置的延迟时间过期了。还有另外一个场景,线程并不阻塞并且持续的发送数据包,如果成功发完就正经关闭,然后连接变为time-wait状态。如果没发完过期了,就直接发RST然后抛弃剩余的包。

开启改参数并不能适用于所有的场景,在HAProxy or Nginx 的服务器上可以在确认合适的情况下开启。

net.ipv4.tcp_tw_reuse

time-wait状态的存在避免了延迟包被无关的连接处理的情况,但是在特定场景下,我们可以认为新的连接不会处理老连接的包。

RFC 1323 提供了一系列在高并发的情况下的提升性能的扩展。这其中就包含两个四字节的timestamp fields字段,前两个用于记录发送包的时间戳,后两个记录从远端收到的最新的时间戳。

开启了 net.ipv4.tcp_tw_reuse 参数后,如果linux收到一个明显比上个连接最后一个包时间晚的请求,那linux就会复用这个处于time-wait状态的连接。大概一秒钟后time-wait的连接就能继续使用

这样能确保合法么?time-wait状态是为了避免收到一个其他连接请求的包,但是因为timestamp fields字段的存在,类似的包会因为超时而被丢弃掉。

另外一个原则是为了确保对端不会因为最后一个ACK包的丢失而导致连接一直处于LAST-ACK状态。对端会一直发送FIN包知道以下几种场景:

1:主动放弃(并且关闭连接)

2:收到了期望的ACK

3:收到RST包

如果FIN包被及时接收了,本端连接仍然保持在time-wait状态,然后ACK包也会照常发送。

一旦新的连接代替了time-wait的坑位,新链接的SYN包会被返回FIN,然后FIN包会被应答RST。然后对端就脱离了LAST-ACK状态了。连接就会马上进行重建

last-ack-reuse.png

net.ipv4.tcp_tw_recycle

此机制还依赖于timestamp选项,但会影响传入和传出连接。这在服务器通常首先关闭连接时更实用。

TIME-WAIT状态计划更快到期:它将在RTO(从RTT及其方差计算的重传超时)之后被删除。 你可以通过ss命令找到活动连接的相应值。

参数开启后,linux会记录请求包最新的时间戳,然后老的都删除。但是远程主机是一个NAT设备,NAT后的不同主机的时间戳可能是乱序的,所以就会导致丢包的问题。

Linux 4.10 后,linux随机生成时间戳的偏移量,所以这两个参数就废了。在4.12后这个参数被移除了。

总结一哈

通用的解决方案毫无疑问是添加足够的可用连接数量,这样就不会因为time-wait焦头烂额。

在服务端,不要开启 net.ipv4.tcp_tw_recycle 参数,开启net.ipv4.tcp_tw_reuse 参数对入栈连接也没什么用。

在客户端,开启net.ipv4.tcp_tw_reuse 也算一个相对合理的方法。开启net.ipv4.tcp_tw_recycle也没什么大用。

制订协议的时候,不要让客户端先关闭连接。这样客户端就不用处理time-wait状态的连接了。服务端处理这些问题更合理的。

最后引用一句 W. Richard StevensUnix Network Programming中的一句话:

time-wait状态是我们的好朋友是来帮助我们的(比如让一些老的重复的网络包过期掉)。相比极力避免折这种状态,我们更应该充分理解它。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343