我正在尝试让我正在编写的 HTTP 服务器在重负载下表现良好,但是我遇到了一些我不太理解的奇怪行为。
我的测试包括ab
在环回接口上以 1000 ( ) 的并发级别使用(Apache 基准程序)ab -n 50000 -c 1000 http://localhost:8080/apa
,同时跟踪服务器进程。Strace 既能很好地减慢处理速度,让问题很容易重现,也能让我在某种程度上调试服务器内部完成后的工作。tcpdump
我还在测试运行时捕获网络流量。
发生的事情是ab
在测试中停止运行一段时间,抱怨连接返回ECONNRESET
,我觉得这有点奇怪。我可以很容易地接受连接超时,因为服务器可能根本没有带宽来处理它们,但不应该合理地返回ETIMEDOUT
,或者即使ECONNREFUSED
不是所有连接都可以被接受?
我使用 Wireshark 提取构成第一个连接的数据包返回ECONNRESET
,它的简要数据包列表如下所示:(
此连接
的整个tcpdump
文件可在此处获得。)
从这个转储中可以看到,连接被接受(在几次SYN
重传之后),然后请求被重传几次,然后服务器重置连接。我想知道,什么可能导致这种情况发生?通常,Linux 的 TCP 实现会在读取过程之前 ACK 数据,甚至选择接收数据,只要它们在 TCP 窗口中有空间,那么这里为什么不这样做呢?是否有某种共享缓冲区快用完了?最重要的是,为什么内核RST
会突然响应一个数据包,而不是简单地等待并让客户端进一步重新传输?
作为记录,进程的 strace 表明它甚至从不接受来自此连接中的端口(端口 56946)的连接,所以这似乎是 Linux 自己做的事情。还值得注意的是,只要 ab 的并发级别足够低,服务器就可以很好地工作(它可以很好地工作到大约 100,然后在 100-500 之间的某个地方开始间歇性失败),并且它的请求吞吐量是相当恒定的无论并发级别如何(只要不被跟踪,它每秒处理 6000-7000 个请求)。我没有发现问题发生的频率与我的积压设置之间有任何特定的相关性listen()
(我目前使用的是 128,但我尝试了高达 1024 似乎没有任何区别)。
万一这很重要,我在这个 AMD64 机器上运行 Linux 3.2.0。