2009-08-01 25 views
32

Ilekroć używam "ab" do testowania serwera WWW, będzie on blokował się przez pewien czas po wysłaniu dużej liczby żądań, aby kontynuować po 20 sekundach.Program "ab" zawiesza się po wielu prośbach, dlaczego?

Rozważmy następujący symulator serwera HTTP, napisany w 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 

Następnie uruchom ab następująco:

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

ciągu pierwszych kilku sekund, ab wykonuje swoją pracę jak to miało i używa 100% procesora:

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

Po około 13500 żądaniach zużycie procesora systemowego spada o 0%. ab wydaje się być na coś zamrożony. Problem nie znajduje się na serwerze, ponieważ w tej chwili serwer wywołuje funkcję accept(). Po około 20 sekundach ab kontynuuje, jak gdyby nic się nie stało, i ponownie użyje 100% CPU, tylko po to, aby zamrozić ponownie po kilku sekundach.

Podejrzewam, że coś w jądrze dławi połączenia, ale co i dlaczego? Używam systemu OS X Leopard. Podobne zachowanie zaobserwowałem również w Linuksie, choć zamrożenie odbywa się przy znacznie większej liczbie żądań i nie zdarza się tak często.

Ten problem uniemożliwia mi wykonywanie dużych testów porównawczych HTTP.

Odpowiedz

47

Wygląda na to, że kończy Ci się ephemeral ports. Aby to sprawdzić, użyj polecenia netstat i wyszukaj kilka tysięcy portów w stanie TIME_WAIT.

W systemie Mac OS X domyślny zakres portów efemerycznych wynosi 49152 do 65535, co daje łącznie 16384 porty. Można to sprawdzić za pomocą polecenia sysctl:

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

Gdy zabraknie portów efemerycznych, będzie normalnie trzeba czekać aż stan TIME_WAIT wygasa (2 * maksymalną żywotność segmentu), aż można ponownie użyć konkretnego numeru portu . Możesz podwoić liczbę portów, zmieniając zakres na 32768, który jest domyślny w systemach Linux i Solaris. (Maksymalna liczba port 65535, więc nie można zwiększyć górną.)

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

Zauważ, że official range designated by IANA to 49152 do 65535, a niektóre zapory może zakładać, że porty przypisane dynamicznie spadać w tym przedziale. Konieczne może być ponowne skonfigurowanie zapory sieciowej w celu korzystania z większego zakresu poza siecią lokalną.

Możliwe jest również zmniejszenie maksymalnego czasu życia segmentu (sysctl net.inet.tcp.msl na Mac OS X), który kontroluje czas trwania stanu TIME_WAIT, ale jest to niebezpieczne, ponieważ może spowodować, że starsze połączenia zostaną pomieszane z nowszymi, które są używając tego samego numeru portu. Istnieją również pewne sztuczki wiążące się z konkretnymi portami za pomocą opcji SO_REUSEADDR lub zamykanie za pomocą opcji SO_LINGER, ale mogą one również powodować pomieszanie starych i nowych połączeń, więc generalnie uważa się je za złe pomysły.

+1

Tak, to wszystko. Zmieniłem MSL postępując zgodnie z instrukcjami na http://www.brianp.net/2008/10/03/changing-the-length-of-the-time_wait-state-on-mac-os-x/ i wszystko działa teraz. Dzięki! – Hongli

+0

Dziękuję bardzo, to naprawił ten sam problem, który miałem. –

+0

Tutaj myślałem, że to problem 'Golang' .. kiedy zamroził co 16000 wniosków z' ab' – kouton

16

Zamiast zwiększać liczbę portów, zmienić długość TIME_WAIT na Mac OS X.

Działa tylko w rozwoju, ale teraz mogę zapytać ab tak wielu wniosków, jak chcę bez limitu czasu.

ustawić domyślny limit czasu do 1000ms tak:

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

Strona brianp.net wspomniano w innych odpowiedzi nie jest już dostępny. Możesz go pobrać z internet archive.

Powiązane problemy