'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% CPU:
Benchmarking 127.0.0.1 (be patient)
Completed 4500 requests
Completed 9000 requests
Completed 13500 requests
Приблизительно после 13500 запросов загрузка ЦП системы падает до 0%. Аб, кажется, что-то застыл. Проблема не в сервере, потому что в этот момент сервер вызывает accept(). Примерно через 20 секунд ab продолжается, как будто ничего не произошло, и снова использует 100% CPU, только через несколько секунд снова останавливается.
Я подозреваю, что что-то в ядре дросселирует соединения, но что и почему? Я использую OS X Leopard. Я видел подобное поведение и в Linux, хотя зависание происходит при гораздо большем количестве запросов и не так часто.
Эта проблема не позволяет мне запускать большие тесты HTTP.
2 ответа
Похоже, у вас кончились эфемерные порты. Чтобы проверить, используйте 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, и некоторые брандмауэры могут предполагать, что динамически назначенные порты попадают в этот диапазон. Возможно, вам придется перенастроить брандмауэр, чтобы использовать больший диапазон за пределами вашей локальной сети.
Также возможно уменьшить максимальное время жизни сегмента (sysctl net.inet.tcp.msl
в Mac OS X), который контролирует продолжительность TIME_WAIT
состояние, но это опасно, так как это может привести к тому, что старые соединения будут перепутаны с новыми, которые используют тот же номер порта. Есть также некоторые приемы, связанные с привязкой к конкретным портам с помощью SO_REUSEADDR
вариант или закрытие с SO_LINGER
вариант, но это также может привести к путанице старых и новых соединений, поэтому, как правило, считаются плохими идеями.
Вместо увеличения количества портов измените длину TIME_WAIT
в Mac OS X.
Это работает только в разработке, но теперь я могу спросить ab
для столько запросов, сколько я хочу без тайм-аута.
Установите время ожидания по умолчанию на 1000 мс, например:
$ sudo sysctl -w net.inet.tcp.msl=1000
net.inet.tcp.msl: 15000 -> 1000
Страница brianp.net, упомянутая в другом ответе, больше не доступна. Вы можете получить его из интернет-архива.
Другой вариант решения этой проблемы - включить HTTP KeepAlive, добавив "-k"
вариант. Это заставит ab повторно использовать TCP-соединения и, как следствие, не исчерпает все доступные порты. Например:
ab -n 45000 -c 10 -k http://127.0.0.1:3000/