'ab'程序在收到大量请求后会冻结,为什么?
-
06-07-2019 - |
题
每当我使用'ab'对Web服务器进行基准测试时,它会在发送大量请求后冻结一段时间,但只会在20秒左右后继续。
考虑以下用Ruby编写的HTTP服务器模拟器:
require 'socket'
RESPONSE = "HTTP/1.1 200 OK\r\n" +
"Connection: close\r\n" +
"\r\n" +
"\r\n"
buffer = ""
server = TCPServer.new("127.0.0.1", 3000) # Create TCP server at port 3000.
server.listen(1024) # Set backlog to 1024.
while true
client = server.accept # Accept new client.
client.write(RESPONSE) # Write a stock "HTTP" response.
client.close_write # Shutdown write part of the socket.
client.read(nil, buffer) # Read all data from the socket.
client.close # Close it.
end
然后按如下方式运行ab:
ab -n 45000 -c 10 http://127.0.0.1:3000/
在最初的几秒钟内,ab按照预期完成其工作并使用100%CPU:
Benchmarking 127.0.0.1 (be patient)
Completed 4500 requests
Completed 9000 requests
Completed 13500 requests
在大约13500个请求之后,系统CPU使用率降至0%。 ab似乎被某种东西冻结了。问题不在服务器中,因为此时服务器正在调用accept()。大约20秒后,ab继续,好像什么也没发生,并且会再次使用100%CPU,只能在几秒钟后再次冻结。
我怀疑内核中的某些内容会限制连接,但是为什么以及为什么?我正在使用OS X Leopard。我在Linux上也看到了类似的行为,虽然冻结发生在更多的请求中,并且不会经常发生。
此问题阻止我运行大型HTTP基准测试。
解决方案
听起来你的临时端口已经不多了。要检查,请使用 netstat
命令并在 TIME_WAIT
状态。
在Mac OS X上,默认的短暂端口范围是49152到65535,总共16384个端口。您可以使用 sysctl
命令:
$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last net.inet.ip.portrange.first: 49152 net.inet.ip.portrange.last: 65535
一旦用完临时端口,通常需要等到 TIME_WAIT
状态到期(2 *最大段生存期),直到您可以重用特定的端口号。您可以通过将范围更改为32768(这是Linux和Solaris上的默认值)来增加端口数。 (最大端口号为65535,因此无法增加高端。)
$ sudo sysctl -w net.inet.ip.portrange.first=32768 net.inet.ip.portrange.first: 49152 -> 32768
请注意,IANA指定的官方范围为49152至65535,以及一些防火墙可以假设动态分配的端口在该范围内。您可能需要重新配置防火墙,以便在本地网络之外使用更大的范围。
还可以减少最大段寿命(Mac OS X上的 sysctl net.inet.tcp.msl
),它控制 TIME_WAIT
状态的持续时间,但这很危险,因为它可能导致较旧的连接与使用相同端口号的较新连接混淆。还有一些技巧涉及使用 SO_REUSEADDR
选项绑定到特定端口,或者使用 SO_LINGER
选项关闭,但这些也可能导致新旧连接混淆,所以通常被认为是坏主意。
其他提示
不要增加端口数,而是在Mac OS X上更改 TIME_WAIT
的长度。
这只适用于开发,但我现在可以向 ab
索取尽可能多的请求,而不会超时。
将默认超时设置为1000毫秒,如下所示:
$ sudo sysctl -w net.inet.tcp.msl=1000
net.inet.tcp.msl: 15000 -> 1000
其他答案中提到的brianp.net页面已不再可用。您可以从互联网档案。
解决此问题的另一个选择是通过添加" -k"
选项来启用HTTP KeepAlive。这将使ab重新使用TCP连接,因此它不会耗尽所有可用端口。例如:
ab -n 45000 -c 10 -k http://127.0.0.1:3000/