il programma “ab” si blocca dopo un sacco di richieste, perché?

Ogni volta che uso ‘ab’ per valutare un server web, si bloccherà per un po ‘dopo aver inviato un sacco di richieste, solo per continuare dopo circa 20 secondi.

Si consideri il seguente simulatore di server HTTP, scritto 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 

Quindi eseguo ab come segue:

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

Durante i primi secondi, ab fa il suo lavoro come dovrebbe e usa il 100% della CPU:

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

Dopo circa 13500 richieste, l’utilizzo della CPU del sistema scende allo 0%. ab sembra essere congelato su qualcosa. Il problema non è nel server perché in questo momento il server sta chiamando accept (). Dopo circa 20 secondi, ab continua come se nulla fosse accaduto e utilizzerà nuovamente il 100% della CPU, solo per congelare di nuovo dopo alcuni secondi.

Ho il sospetto che qualcosa nel kernel stia limitando le connessioni, ma cosa e perché? Sto usando OS X Leopard. Ho visto un comportamento simile anche su Linux, anche se il freeze si verifica a un numero molto più grande di richieste e non accade così spesso.

Questo problema mi impedisce di eseguire benchmark HTTP di grandi dimensioni.

Sembra che tu stia finendo le porte effimere . Per verificare, utilizzare il comando netstat e cercare diverse migliaia di porte nello stato TIME_WAIT .

Su Mac OS X l’intervallo di porte temporanee predefinito è compreso tra 49152 e 65535, per un totale di 16384 porte. Puoi verificarlo con il comando sysctl :

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

Una volta esaurite le porte temporanee, normalmente è necessario attendere lo scadere dello stato TIME_WAIT (2 * durata massima del segmento) finché non si riutilizza un determinato numero di porta. È ansible raddoppiare il numero di porte modificando l’intervallo per iniziare a 32768, che è l’impostazione predefinita su Linux e Solaris. (Il numero massimo di porte è 65535, quindi non è ansible aumentare la fascia alta.)

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

Notare che l’ intervallo ufficiale designato da IANA è compreso tra 49152 e 65535 e che alcuni firewall possono assumere che le porte assegnate dynamicmente rientrino in tale intervallo. Potrebbe essere necessario riconfigurare il firewall per utilizzare un intervallo più ampio al di fuori della rete locale.

È anche ansible ridurre la durata massima del segmento ( sysctl net.inet.tcp.msl su Mac OS X), che controlla la durata dello stato TIME_WAIT , ma ciò è pericoloso in quanto potrebbe causare l’ sysctl net.inet.tcp.msl vecchie connessioni con i più recenti quelli che utilizzano lo stesso numero di porta. Ci sono anche alcuni trucchi che riguardano il binding a porte specifiche con l’opzione SO_REUSEADDR , o la chiusura con l’opzione SO_LINGER , ma anche quelli potrebbero causare la SO_LINGER vecchie e nuove connessioni, quindi sono generalmente considerate cattive idee.

Invece di aumentare il numero di porte, modifica la durata di TIME_WAIT su Mac OS X.

Questo funziona solo in fase di sviluppo, ma ora posso chiedere a quante richieste desidero senza scadenze.

Imposta il timeout predefinito su 1000ms in questo modo:

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

La pagina di brianp.net citata nell’altra risposta non è più disponibile. Puoi recuperarlo dall’archivio internet .