【问题标题】:Linux TCP weirdly unresponsive when under heavy load在重负载下,Linux TCP 奇怪地无响应
【发布时间】:2012-12-15 03:39:12
【问题描述】:

我正在尝试让我正在编写的 HTTP 服务器在负载过重时表现良好,但我遇到了一些我不太理解的奇怪行为。

我的测试包括在并发级别为 1000 (ab -n 50000 -c 1000 http://localhost:8080/apa) 的环回接口上使用 ab(Apache 基准程序),同时跟踪服务器进程。 Strace 既能很好地减慢处理速度,使问题很容易重现,也能让我在某种程度上调试服务器内部完成后的工作。我还在测试运行时使用tcpdump 捕获网络流量。

发生的情况是ab 在测试中停止运行一段时间,并抱怨连接返回了ECONNRESET,我觉得这有点奇怪。我可以很容易地接受连接超时,因为服务器可能根本没有带宽来处理它们,但是如果不是所有连接都可以接受,那不应该合理地返回 ETIMEDOUT 甚至 ECONNREFUSED 吗?

我使用Wireshark提取构成第一个连接的数据包返回ECONNRESET,它的简要数据包列表如下所示: (此连接的整个tcpdump 文件可用here。)

从这个转储中可以看到,连接被接受(经过几次SYN重传),然后请求被重传几次,然后服务器重置连接。我想知道,什么可能导致这种情况发生?通常,Linux 的 TCP 实现会在读取过程之前 ACK 数据,甚至选择接收数据,只要它们在 TCP 窗口中有空间,那么这里为什么不这样做呢?是否有某种共享缓冲区快用完了?最重要的是,为什么内核会突然响应RST 数据包,而不是简单地等待并让客户端进一步重新传输?

为了记录,进程的 strace 表明它甚至从不接受来自此连接中的端口(端口 56946)的连接,所以这似乎是 Linux 自己做的事情。还值得注意的是,只要 ab 的并发级别足够低,服务器就可以很好地工作(它可以很好地工作到大约 100,然后在 100-500 之间的某个地方开始间歇性失败),并且它的请求吞吐量是相当恒定的无论并发级别如何(只要不被跟踪,它每秒处理 6000-7000 个请求)。我没有发现问题发生的频率与我的积压设置为listen() 之间有任何特定的相关性(我目前使用的是 128,但我尝试了高达 1024 似乎没有任何区别)。

以防万一,我在这个 AMD64 机器上运行 Linux 3.2.0。

【问题讨论】:

    标签: linux http networking tcp load-testing


    【解决方案1】:
    1. 积压队列已满:因此SYN重传。

    2. 然后一个插槽可用:因此SYN/ACK

    3. 然后 GET 被发送,然后是四次重传,我无法解释。

    4. 然后服务器放弃并重置连接。

    我怀疑您的服务器存在并发或吞吐量问题,导致您无法足够快地接受连接。您应该有一个线程专门用于调用 accept() 并启动另一个线程来处理接受的套接字,或者将作业排队以将其处理到线程池。然后我会推测 Linux 会重置积压队列中正在接收 I/O 重试的连接上的连接,但这只是一个猜测。

    【讨论】:

    • 不过,我想知道的是那些 GET 重传。我理解 SYN 延迟没有问题。通常,Linux 会在从套接字读取数据之前,甚至在新连接被accept()ed 之前确认推送的数据。什么样的缓冲区正在填满这种情况?如果客户端 SYN 本身已被确认,并且连接因此被正确跟踪(对吗?),为什么服务器 RST 呢?
    • @Dolda 这就是我猜测的主题。看起来很像您以“通常”开头的第二句话不是真的,不是吗?
    • 确实如此,但我想知道它是如何偏离常态的。 :) 正如我所说,Linux 通常会在新套接字被接受之前确认数据,因此您关于在积压队列中重置正在接收 I/O 的连接的猜测似乎不是答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-05
    相关资源
    最近更新 更多