TIME_WAIT 连接在超时期限到期后未被清理

TIME_WAIT 连接在超时期限到期后未被清理

我正在对我的一台服务器进行压力测试,通过向它发送恒定的新网络连接流,设置tcp_fin_timeout为 60,因此,如果我每秒发送 100 个请求的恒定流,我期望看到处于某种TIME_WAIT状态的连接数滚动平均值为 6000(60 * 100),这种情况确实发生了,但是查看netstat(使用 -o)以查看计时器,我看到如下连接:

TIME_WAIT   timewait (0.00/0/0)

它们的超时已过期但连接仍然挂起,最终我用完了连接。有人知道为什么这些连接没有被清理吗?如果我停止创建新连接,它们最终会消失,但当我不断创建新连接时,它们不会消失,似乎内核没有机会清理它们?我是否需要设置其他一些配置选项,以便在连接过期后立即删除它们?

服务器运行的是 Ubuntu,我的 Web 服务器是 nginx。此外,它还具有带连接跟踪的 iptables,不确定这是否会导致这些TIME_WAIT连接继续存在。

谢谢马克。

答案1

这个问题很有趣,因为我自己也经常想这个问题。我做了一些测试,发现了一些有趣的结果。如果我打开一个到服务器的连接并等待 60 秒,它总是会被清理(永远不会达到 0.00/0/0)。如果我打开 100 个连接,它们也会在 60 秒后被清理。如果我打开 101 个连接,我将开始看到处于您提到的状态的连接(我以前也见过)。而且它们似乎持续大约 120 秒或 2xMSL(即 60),无论 fin_timeout 设置为什么。我在内核源代码中做了一些挖掘,找到了我认为的“原因”。似乎有一些代码试图限制每个“周期”发生的套接字收割量。周期频率本身是根据 HZ 的尺度设置的:

linux-source-2.6.38/include/net/inet_timewait_sock.h:
     35 #define INET_TWDR_RECYCLE_SLOTS_LOG     5
     36 #define INET_TWDR_RECYCLE_SLOTS         (1 << INET_TWDR_RECYCLE_SLOTS_LOG)
     37 
     38 /*
     39  * If time > 4sec, it is "slow" path, no recycling is required,
     40  * so that we select tick to get range about 4 seconds.
     41  */
     42 #if HZ <= 16 || HZ > 4096
     43 # error Unsupported: HZ <= 16 or HZ > 4096
     44 #elif HZ <= 32
     45 # define INET_TWDR_RECYCLE_TICK (5 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     46 #elif HZ <= 64
     47 # define INET_TWDR_RECYCLE_TICK (6 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     48 #elif HZ <= 128
     49 # define INET_TWDR_RECYCLE_TICK (7 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     50 #elif HZ <= 256
     51 # define INET_TWDR_RECYCLE_TICK (8 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     52 #elif HZ <= 512
     53 # define INET_TWDR_RECYCLE_TICK (9 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     54 #elif HZ <= 1024
     55 # define INET_TWDR_RECYCLE_TICK (10 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     56 #elif HZ <= 2048
     57 # define INET_TWDR_RECYCLE_TICK (11 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     58 #else
     59 # define INET_TWDR_RECYCLE_TICK (12 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG)
     60 #endif
     61 
     62 /* TIME_WAIT reaping mechanism. */
     63 #define INET_TWDR_TWKILL_SLOTS  8 /* Please keep this a power of 2. */
The number of slots is also set here:
     65 #define INET_TWDR_TWKILL_QUOTA 100

在实际的 timewait 代码中,你可以看到它使用引号来停止终止 TIME_WAIT 连接(如果已经完成太多):

linux-source-2.6.38/net/ipv4/inet_timewait_sock.c:
    213 static int inet_twdr_do_twkill_work(struct inet_timewait_death_row *twdr,
    214                                     const int slot)
    215 {
...
    240                 if (killed > INET_TWDR_TWKILL_QUOTA) {
    241                         ret = 1;
    242                         break;
    243                 }

这里有更多关于为什么将 HZ 设置为如下设置的信息: http://kerneltrap.org/node/5411 但增加它并不罕见。但我认为,更常见的做法是启用 tw_reuse/recycling 来绕过这个 bucket/quota 机制(现在我读到它时,这似乎让我感到困惑,增加 HZ 会是一个更安全、更干净的解决方案)。我把这个作为答案发布出来,但我认为这里可以有更多关于修复它的“正确方法”的讨论。谢谢你的有趣问题!

答案2

不要使用tcp_tw_recycle = 1以下命令:

tcp_tw_reuse = 1

当您使用 NAT 或负载平衡时,Recyle 报告已损坏并且在某些情况下不起作用。

答案3

net.ipv4.tcp_fin_timeout 默认为 60 秒。我一直不明白为什么套接字停留在 TIME_WAIT 状态的时间会超过此限制。

tcp_tw_recycle 据说坏了,我不知道,因为我没用过。你可能需要将 tcp_tw_reuse 设置为 1,但据说这会导致 NAT 出现问题。

相关内容