我正在对我的一台服务器进行压力测试,通过向它发送恒定的新网络连接流,设置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 出现问题。