2009-08-01 4 views
32

Immer, wenn ich 'ab' verwende, um einen Webserver zu benchmarken, wird er eine Zeit lang eingefroren, nachdem er viele Anfragen gesendet hat, nur um nach etwa 20 Sekunden fortzufahren.'ab' Programm friert nach vielen Anfragen ein, warum?

den folgenden HTTP-Server Simulator Betrachten wir in Ruby geschrieben:

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 

ich dann ab laufen wie folgt:

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

Während der ersten paar Sekunden, tut ab, seine Arbeit als es soll und verwendet 100% CPU:

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

Nach etwa 13500 Anfragen sinkt System CPU-Auslastung t o 0%. ab scheint auf etwas eingefroren zu sein. Das Problem liegt nicht auf dem Server, da der Server in diesem Moment accept() aufruft. Nach etwa 20 Sekunden wird der Vorgang fortgesetzt, als ob nichts passiert wäre, und er wird wieder 100% CPU verwenden, nur um nach einigen Sekunden wieder einzufrieren.

Ich vermute, etwas im Kernel drosselt Verbindungen, aber was und warum? Ich benutze OS X Leopard. Ich habe ähnliches Verhalten auch unter Linux gesehen, obwohl das Einfrieren bei einer viel größeren Anzahl von Anfragen passiert und nicht so häufig vorkommt.

Dieses Problem verhindert, dass ich große HTTP-Benchmarks ausführen.

Antwort

47

Es klingt, als ob Sie von ephemeral ports ausgehen. Um dies zu überprüfen, verwenden Sie den Befehl netstat und suchen Sie nach mehreren tausend Ports im TIME_WAIT-Status.

Unter Mac OS X beträgt der standardmäßige ephemere Portbereich 49152 bis 65535, also insgesamt 16384 Ports. Sie können dies mit dem sysctl Befehl:

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

Sobald Sie aus temporären Ports ausführen, werden Sie in der Regel müssen warten, bis der TIME_WAIT Zustand abläuft (2 * maximale Segmentlebenszeit), bis Sie eine bestimmte Portnummer wiederverwenden können . Sie können die Anzahl der Ports verdoppeln, indem Sie den Bereich für den Start bei 32768 ändern. Dies ist die Standardeinstellung für Linux und Solaris. (Die maximale Portnummer 65535, so dass Sie nicht die High-End erhöhen.)

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

Beachten Sie, dass die official range designated by IANA ist 49.152-65.535, und einige Firewalls annehmen kann, die dynamisch zugewiesenen Ports innerhalb dieses Bereichs fallen. Möglicherweise müssen Sie Ihre Firewall neu konfigurieren, um einen größeren Bereich außerhalb Ihres lokalen Netzwerks nutzen zu können.

Es ist auch möglich, die maximale Segmentlebensdauer (sysctl net.inet.tcp.msl auf Mac OS X) zu reduzieren, was die Dauer des TIME_WAIT Staates kontrolliert, aber das ist gefährlich, da es ältere Verbindungen dazu führen könnte, mit neueren zu verwechseln, die Verwenden Sie die gleiche Portnummer. Es gibt auch einige Tricks, die das Binden an bestimmte Ports mit der Option SO_REUSEADDR oder das Schließen mit der Option SO_LINGER einschließen, aber diese können auch dazu führen, dass alte und neue Verbindungen durcheinander geraten, so dass sie im Allgemeinen als schlechte Ideen betrachtet werden.

+1

Ja, das ist es. Ich änderte das MSL, indem ich die Anweisungen an http://www.brianp.net/2008/10/03/changing-the-length-of-the-time_wait-state-on-mac-os-x/ befolgte und alles funktioniert jetzt. Vielen Dank! – Hongli

+0

Vielen Dank, dies behebt das gleiche Problem, das ich hatte. –

+0

Hier dachte ich, es sei ein 'Golang'-Problem ... als es alle 16000 Anfragen von 'ab' einfror – kouton

16

Statt die Anzahl der Anschlüsse zu erhöhen, ändern Sie die Länge der TIME_WAIT auf Mac OS X.

Dies funktioniert nur in der Entwicklung, aber ich kann jetzt ab für so viele Anfragen fragen, wie ich ohne Zeit wollen.

Stellen Sie die Standard-Timeout wie so bis 1000 ms:

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

Die brianp.net Seite in der anderen Antwort erwähnt ist nicht mehr verfügbar. Sie können es von der internet archive abrufen.