‚Ab‘ Programm friert nach vielen Anfragen, warum?
-
06-07-2019 - |
Frage
Jedes Mal, wenn ich ‚ab‘ Benchmark einen Web-Server verwenden, wird es für eine Weile einfrieren, nachdem geschickt viele Anfragen haben, erst nach 20 Sekunden andauern oder so.
Betrachten Sie den folgenden HTTP-Server-Simulator, geschrieben 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
Ich laufe ab dann wie folgt:
ab -n 45000 -c 10 http://127.0.0.1:3000/
Während der ersten paar Sekunden ab macht seinen Job als es soll und nutzt 100% CPU:
Benchmarking 127.0.0.1 (be patient)
Completed 4500 requests
Completed 9000 requests
Completed 13500 requests
Nach etwa 13.500 Anfragen, System CPU-Auslastung sinkt 0% bis. ab scheint auf etwas eingefroren werden. Das Problem ist nicht auf dem Server, weil in diesem Moment wird der Server akzeptieren () aufrufen. Nach etwa 20 Sekunden ab weiter, als ob nichts passiert ist, und wird zu 100% CPU wieder verwenden, nur um wieder zu frieren nach einigen Sekunden.
ich etwas im Kernel vermuten Drosselung Verbindungen, aber was und warum? Ich bin mit OS X Leopard. Ich habe auf Linux als auch ein ähnliches Verhalten gesehen, obwohl das Einfrieren bei einer viel größeren Anzahl von Anfragen geschieht und nicht geschieht so oft.
Dieses Problem hindert mich daran, läuft großes HTTP-Benchmarks.
Lösung
Es klingt wie Sie aus ephemeren Ports rel="noreferrer">. Um zu überprüfen, verwenden Sie die netstat
Befehl und schauen für mehrere tausend Ports in der TIME_WAIT
Zustand
Unter Mac OS X der Standard temporäre Portbereich ist 49.152 bis 65.535, für insgesamt 16.384 Ports. Sie können dies mit dem sysctl
Befehl ein:
$ 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 bei 32.768 starten zu ändern, die der Standard unter Linux und Solaris ist. (Die maximale Portnummer 65535, so dass Sie nicht das High-End erhöhen.)
$ sudo sysctl -w net.inet.ip.portrange.first=32768 net.inet.ip.portrange.first: 49152 -> 32768
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 zu verwechseln mit neueren dazu führen könnte, dass verwenden die gleiche Portnummer. Darüber hinaus gibt es einige Tricks, die Bindung an bestimmte Ports mit der SO_REUSEADDR
Option mit oder Schließen der SO_LINGER
Option, aber die könnte auch dazu führen, alte und neue Verbindungen gemischt werden, sind so allgemein schlechte Ideen angesehen werden.
Andere Tipps
Statt die Anzahl der Anschlüsse zu erhöhen, um die Länge von TIME_WAIT
unter Mac OS X ändern
Dies funktioniert nur in der Entwicklung, aber ich kann jetzt ab
für so viele Anfragen fragt, wie ich ohne Zeit will aus.
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-Archiv .
Eine andere Möglichkeit, das Problem zu lösen, ist HTTP-Keep-Alive zu aktivieren, indem Sie die "-k"
Option hinzufügen. Dadurch wird ab, um die TCP-Verbindungen wieder zu verwenden und als Folge wird es nicht alle Ports verfügbar erschöpfen. Zum Beispiel:
ab -n 45000 -c 10 -k http://127.0.0.1:3000/