Программа 'ab' зависает после большого количества запросов, почему?

Всякий раз, когда я использую «ab» для тестирования веб-сервера, он зависает на некоторое время после отправки большого количества запросов, чтобы продолжить работу примерно через 20 секунд.

Рассмотрим следующий симулятор HTTP-сервера, написанный на 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

Затем я запускаю ab следующим образом:

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

Первые несколько секунд ab выполняет свою работу как положено и загружает процессор на 100%:

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

Примерно после 13500 запросов загрузка ЦП системы падает до 0%. ab как будто застыл на чем-то. Проблема не в сервере, потому что в этот момент сервер вызывает accept(). Примерно через 20 секунд ab продолжит работу, как будто ничего не произошло, и снова будет использовать 100% ЦП, но снова зависнет через несколько секунд.

Я подозреваю, что что-то в ядре дросселирует соединения, но что и почему? Я использую ОС Х Леопард. Я видел подобное поведение и в Linux, хотя зависание происходит при гораздо большем количестве запросов и случается не так часто.

Эта проблема не позволяет мне запускать большие тесты HTTP.


person Hongli    schedule 01.08.2009    source источник


Ответы (3)


Похоже, у вас закончились временные порты. Для проверки используйте команду 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, составляет от 49 152 до 65 535, и некоторые брандмауэры могут предполагать, что динамически назначаемые порты попадают в этот диапазон. Возможно, вам придется перенастроить брандмауэр, чтобы использовать больший диапазон за пределами вашей локальной сети.

Также можно уменьшить максимальное время жизни сегмента (sysctl net.inet.tcp.msl в Mac OS X), которое контролирует продолжительность состояния TIME_WAIT, но это опасно, поскольку может привести к тому, что старые соединения будут перепутаны с новыми, использующими тот же порт. номер. Есть также некоторые приемы, связанные с привязкой к определенным портам с помощью параметра SO_REUSEADDR или закрытием с помощью параметра SO_LINGER, но они также могут привести к смешению старых и новых соединений, поэтому обычно считаются плохими идеями.

person mark4o    schedule 01.08.2009
comment
Да это оно. Я изменил MSL, следуя инструкциям на странице brianp.net/2008/10/03/ и теперь все работает. Спасибо! - person Hongli; 02.08.2009
comment
Большое спасибо, это решило ту же проблему, что и у меня. - person Kendall Hopkins; 04.12.2009
comment
Здесь я думал, что это проблема Golang... когда он зависал каждые 16000 запросов от ab - person kouton; 02.08.2014

Вместо увеличения количества портов измените длину TIME_WAIT в Mac OS X.

Это работает только в разработке, но теперь я могу запрашивать у ab столько запросов, сколько захочу, без тайм-аута.

Установите время ожидания по умолчанию на 1000 мс, например:

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

Страница brianp.net, упомянутая в другом ответе, больше недоступна. Вы можете получить его из интернет-архив.

person Frederik    schedule 14.07.2011

Другой вариант решения проблемы — включить HTTP KeepAlive, добавив параметр "-k". Это заставит ab повторно использовать TCP-соединения и, как следствие, не исчерпает все доступные порты. Например:

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

person Juan Moyano    schedule 20.04.2019