'ab' 程序在收到大量请求后冻结,为什么?

发布于 2024-07-29 18:58:51 字数 1321 浏览 3 评论 0原文

每当我使用“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 基准测试。

Whenever I use 'ab' to benchmark a web server, it will freeze for a while after having sent lots of requests, only to continue after 20 seconds or so.

Consider the following HTTP server simulator, written in Ruby:

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

I then run ab as follows:

ab -n 45000 -c 10 http://127.0.0.1:3000/

During the first few seconds, ab does its job as it's supposed to and uses 100% CPU:

Benchmarking 127.0.0.1 (be patient)
Completed 4500 requests
Completed 9000 requests
Completed 13500 requests

After about 13500 requests, system CPU usage drops to 0%. ab seems to be frozen on something. The problem is not in the server because at this moment, the server is calling accept(). After about 20 seconds ab continues as if nothing happened, and will use 100% CPU again, only to freeze again after several seconds.

I suspect something in the kernel is throttling connections, but what and why? I'm using OS X Leopard. I've seen similar behavior on Linux as well, though the freeze happens at a much larger number of requests and doesn't happen so often.

This problem prevents me from running large HTTP benchmarks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浴红衣 2024-08-05 18:58:51

听起来您的临时端口已经用完了。 要进行检查,请使用 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 选项关闭,但这些也可能导致新旧连接混淆,所以通常被认为是坏主意。

It sounds like you are running out of ephemeral ports. To check, use the netstat command and look for several thousand ports in the TIME_WAIT state.

On Mac OS X the default ephemeral port range is 49152 to 65535, for a total of 16384 ports. You can check this with the sysctl command:

$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
net.inet.ip.portrange.first: 49152
net.inet.ip.portrange.last: 65535

Once you run out of ephemeral ports, you will normally need to wait until the TIME_WAIT state expires (2 * maximum segment lifetime) until you can reuse a particular port number. You can double the number of ports by changing the range to start at 32768, which is the default on Linux and Solaris. (The maximum port number is 65535 so you cannot increase the high end.)

$ sudo sysctl -w net.inet.ip.portrange.first=32768
net.inet.ip.portrange.first: 49152 -> 32768

Note that the official range designated by IANA is 49152 to 65535, and some firewalls may assume that dynamically assigned ports fall within that range. You may need to reconfigure your firewall in order to make use of a larger range outside of your local network.

It is also possible to reduce the maximum segment lifetime (sysctl net.inet.tcp.msl on Mac OS X), which controls the duration of the TIME_WAIT state, but this is dangerous as it could cause older connections to get mixed up with newer ones that are using the same port number. There are also some tricks involving binding to specific ports with the SO_REUSEADDR option, or closing with the SO_LINGER option, but those also could cause old and new connections to be mixed up, so are generally considered to be bad ideas.

吻泪 2024-08-05 18:58:51

不要增加端口数量,而是更改 Mac OS X 上的 TIME_WAIT 长度。

这仅在开发中有效,但我现在可以向 ab 请求尽可能多的请求想要没有超时。

将默认超时设置为 1000 毫秒,如下所示:

$ sudo sysctl -w net.inet.tcp.msl=1000
net.inet.tcp.msl: 15000 -> 1000

其他答案中提到的 brianp.net 页面不再可用。 您可以从 互联网存档

Instead of increasing the number of ports, change the length of TIME_WAIT on Mac OS X.

This only works in development, but I can now ask ab for as many requests as I want without timing out.

Set the default timeout to 1000ms like so:

$ sudo sysctl -w net.inet.tcp.msl=1000
net.inet.tcp.msl: 15000 -> 1000

The brianp.net page mentioned in the other answer is no longer available. You can retrieve it from the internet archive.

不知在何时 2024-08-05 18:58:51

解决此问题的另一个选项是通过添加 "-k" 选项来启用 HTTP KeepAlive。 这将使 ab 重新使用 TCP 连接,因此不会耗尽所有可用端口。 例如:

ab -n 45000 -c 10 -k http://127.0.0.1:3000/

Another option to solve the issue is to enable HTTP KeepAlive by adding the "-k" option. This will make ab to re-use the TCP connections and as consequence it won't exhaust all ports available. For example:

ab -n 45000 -c 10 -k http://127.0.0.1:3000/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文