设置TIME_WAIT TCP

78
我们正在调整一个通过TCP接收消息并在内部使用TCP进行某些消息传递的应用程序。在负载测试期间,我们注意到随着对系统的同时请求增加,响应时间会显著降低(然后完全停止)。在此期间,我们看到许多处于TIME_WAIT状态的TCP连接,并有人建议将TIME_WAIT环境变量从其默认值60秒降低到30秒。
我所理解的来看,TIME_WAIT设置实际上是在连接关闭后再次向系统提供TCP资源的时间。
我不是“网络专家”,对这些事情知之甚少。我需要那篇链接文章中的很多内容,但需要简化一下。
  • 我认为我理解了为什么不能将TIME_WAIT值设置为0,但它是否可以安全地设置为5?10呢?什么决定了该值的“安全”设置?
  • 为什么这个值的默认值是60?我猜想比我聪明得多的人选择这个作为合理的默认值有充分的理由。
  • 除了覆盖此值可能带来的潜在风险和好处外,我还应该知道什么?

同时,您也不希望将其设置得太高:https://dev59.com/kXI-5IYBdhLWcg3wiY7a - Pacerier
7个回答

109

TCP连接由元组(源IP,源端口,目标IP,目标端口)指定。

在会话关闭后存在TIME_WAIT状态的原因是因为可能仍有在网络中传输的未处理的数据包(从您发送的或向您请求响应的数据包)。如果您重新创建相同的元组并且其中一个数据包出现,它将被视为与您的连接相关的有效数据包(由于顺序问题,可能会导致错误发生)。

因此,TIME_WAIT时间通常设置为数据包最大寿命的两倍。该值是网络丢弃包之前允许它们到达的最大寿命。

这确保在允许您使用相同元组创建新的连接之前,所有属于先前版本该元组的数据包都已过期。

这通常决定了您应该使用的最小值。最大数据包年龄由网络属性决定,例如卫星寿命比局域网寿命更长,因为数据包要经过更远的距离。


1
我如何确定“最大数据包年龄”?这是由操作系统、网络上的某些东西还是一些软件设置来确定的?顺便说一下,生成大多数这些连接的代码是第三方平台,我们没有源代码。感谢您的出色回答! - Vinnie
5
它的真正名称是最大分段生存时间,即MSL。不确定您是否可以在Windows中更改它,甚至不确定是否应该这样做 - 它应该基于网络特性进行设置。Windows将其设置为120秒。所有TCP参数都在HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters中。 - paxdiablo
不确定您是否可以在Windows中更改此设置,甚至是否应该这样做 - 它应该根据网络特性进行设置。我认为Windows将其设置为120秒。☹ 您绝对可以在Linux中更改它,并且120太长了。我没有测试以确认,但看起来这么长的延迟会使大多数路由器上的P2P变得非常困难。 - Synetech
@Synetech,只有在循环处理大量会话时才会出现问题。否则,会话超时后的清理应该可以解决它。我相信即使是Torrent也会保持会话一段时间,并尽可能重用它们,当然这取决于客户端。 - paxdiablo
你可以在Windows中更改TIME_WAIT的默认值:[https://docs.oracle.com/cd/E26180_01/Search.94/ATGSearchAdmin/html/s1207adjustingtcpsettingsforheavyload01.html] - Per Lindberg
显示剩余2条评论

22
通常情况下,只有发出“主动关闭”的端点应该进入TIME_WAIT状态。因此,如果可能的话,请让您的客户端发出主动关闭,这将使TIME_WAIT留在客户端而不是服务器上。
详情请参见:http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.htmlhttp://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/(后者还解释了由于协议设计未考虑TIME_WAIT而导致无法始终实现的原因)。

2
好的,服务器仍然需要等待来自FIN的ACK,但这应该需要更少的时间。对于发起方关闭会话也是一个好习惯,因为只有它通常知道何时完成。 - paxdiablo
1
第二个链接已经失效,请尝试使用这个链接代替:https://web.archive.org/web/20170521010646/http://www.isi.edu/touch/pubs/infocomm99/infocomm99-web/ - Abhishek Kumar

10

Pax对TIME_WAIT的原因和为什么要小心降低默认设置是正确的。

更好的解决方案是改变套接字起始端使用的端口号。这样做后,您就不需要真正关心单个套接字的等待时间。

对于监听套接字,您可以使用SO_REUSEADDR来允许监听套接字绑定,尽管存在TIME_WAIT套接字。


17
我会点赞任何以“Pax is correct”开头的答案。 :-) - paxdiablo
5
对于非常活跃的机器,拥有数千个活动套接字,实际上可能会耗尽所有的临时端口以等待 TIME_WAIT。一旦发生这种情况,您无法再打开任何连接,直到某些套接字完成等待。减少 TIME_WAIT 的持续时间可以帮助很多。正如 Len Holgate 所提到的,如果可能的话,最好由客户端启动主动关闭,因为这样可以完全避免服务器执行 TIME_WAIT 任务。 - Sam Hanes
1
默认情况下,源端口号会自动变化,这对服务器上的TIME_WAIT状态没有帮助。正确的解决方案是确保客户端先关闭连接。 - user207421

4
在Windows中,你可以通过注册表更改它:can change
; Set the TIME_WAIT delay to 30 seconds (0x1E)

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters]
"TcpTimedWaitDelay"=dword:0000001E

6
设置tcp_fin_timeout不影响time_wait状态 - 这是一个常见的误解。它用于完全不同的事情(显然是FIN超时)。许多指南等指向此设置,但它们是错误的。查看tcp.h,你会发现它已经被硬编码(Linux)。 - mcauth

2
设置tcp_reuse比更改time_wait更有用,只要您有该参数(内核3.2及以上版本,不幸的是这使所有RHEL和XenServer版本都不符合要求)。
特别是对于VPN连接的用户来说,降低该值可能会导致出站连接上代理隧道的不断重建。使用默认的Netscaler(XenServer)配置,其低于默认的Linux配置,Chrome有时需要重新创建代理隧道多达十几次才能检索一个网页。不重试的应用程序(如Maven和Eclipse P2)将直接失败。
该参数的原始动机(避免重复)已被TCP RFC废除,该RFC规定在所有TCP请求中包含时间戳。

0

我一直在使用一个带有20个线程的测试程序对Linux服务器应用进行负载测试。

在959,000次连接/关闭循环中,我遇到了44,000个连接失败和成千上万个处于TIME_WAIT状态的套接字。

在关闭调用之前,我将SO_LINGER设置为0,并在随后的测试程序运行中没有遇到连接失败,并且TIME_WAIT状态下的套接字少于20个。


2
你还有可能面临数据丢失的风险。这不是正确的解决方案。SO_LINGER 不是可以随意处理的东西。正确的解决方案是确保客户端先关闭,通常通过在客户端进行连接池化并编写服务器以处理每个连接的多个请求来实现。就像 HTTP 1.1 一样。 - user207421
我的回答应该更清楚一些。我并不主张将停留时间设置为0,只是指出了我在Windows上观察到的问题。在Linux上,这个问题并没有发生。因此,它是系统相关的,并且很可能取决于哪些系统设置是活动的。 - Michael Taylor
它不依赖于系统。像这样重置套接字会丢失所有正在传输的数据。 - user207421

-2

TIME_WAIT 可能不是罪魁祸首。

int listen(int sockfd, int backlog);

根据Unix网络编程卷1的定义,backlog被定义为已完成连接队列和未完成连接队列的总和。
假设backlog为5。如果您有3个已完成连接(ESTABLISHED状态)和2个未完成连接(SYN_RCVD状态),并且还有另一个带有SYN的连接请求。TCP堆栈会忽略SYN数据包,因为它知道它将在其他时间重新传输。这可能会导致性能下降。
至少这就是我一直在阅读的内容。;)

我相信积压队列仅适用于尚未达到已建立状态的连接;一旦它们达到该状态,它们将从队列中移除;它们只会阻塞更多的传入连接,直到(SYN、SYN/ACK、ACK)握手完成,基本上是一旦服务器从accept()返回。 - paxdiablo
1
不,监听队列仅用于尚未完全建立的连接;即它们已到达TCP/IP堆栈,但尚未被“接受”。如果您的监听队列太小,则服务器将拒绝连接,如果连接速度比它可以接受的速度更快。 - Len Holgate
2
一个小误解。“已完成连接队列。该队列包含每个完成三次握手的连接的条目。套接字处于已建立状态。每次调用accept()将移除队列的前面条目。” http://www.sean.de/Solaris/soltune.html - yogman
1
@LenHolgate 一个未被接受的连接仍然可以完全建立,这样的连接会进入后备队列。还有其他用于不完整连接的队列。是否拒绝或忽略会导致超出后备队列的传入连接取决于系统:Windows会拒绝,Unix等则会忽略。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接