2009-08-01 24 views
32

Cada vez que uso 'ab' para comparar un servidor web, se congelará por un tiempo después de haber enviado muchas solicitudes, solo para continuar después de 20 segundos más o menos.programa 'ab' se congela después de muchas solicitudes, ¿por qué?

Considérese el siguiente simulador de servidor HTTP, escrita en 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 

entonces corro ab de la siguiente manera:

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

Durante los primeros segundos, ab hace su trabajo como se supone que debe y utiliza 100% de CPU:

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

Después de aproximadamente 13500 solicitudes, el uso de la CPU del sistema cae t o 0%. ab parece estar congelado en algo. El problema no está en el servidor porque en este momento, el servidor está llamando a accept(). Después de unos 20 segundos ab continúa como si nada hubiera sucedido, y usará 100% de CPU nuevamente, solo para congelar nuevamente después de varios segundos.

Sospecho que algo en el kernel está limitando las conexiones, pero ¿qué y por qué? Estoy usando OS X Leopard. También he visto un comportamiento similar en Linux, aunque la congelación ocurre en un número mucho mayor de solicitudes y no ocurre con tanta frecuencia.

Este problema me impide ejecutar pruebas de referencia HTTP de gran tamaño.

Respuesta

47

Parece que se está quedando sin ephemeral ports. Para verificar, use el comando netstat y busque varios miles de puertos en el estado TIME_WAIT.

En Mac OS X, el rango predeterminado de puertos efímeros es 49152 a 65535, para un total de 16384 puertos. Esto se puede comprobar con el 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 vez que se queda sin puertos efímeros, por lo general hay que esperar hasta que el estado TIME_WAIT expira (2 x duración máxima del segmento) hasta que pueda volver a utilizar un número de puerto en particular . Puede duplicar la cantidad de puertos cambiando el rango para comenzar en 32768, que es el valor predeterminado en Linux y Solaris. (El número máximo de puerto es 65535 por lo que no se puede aumentar la gama alta.)

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

Tenga en cuenta que la official range designated by IANA es 49152 y 65535 y algunos servidores de seguridad puede suponer que los puertos asignados dinámicamente caen dentro de ese rango. Es posible que necesite reconfigurar su firewall para utilizar un rango mayor fuera de su red local.

También es posible reducir el tiempo de vida máximo del segmento (sysctl net.inet.tcp.msl en Mac OS X), que controla la duración del estado TIME_WAIT, pero esto es peligroso, ya que podría provocar que las conexiones de más edad a se mezclan con los más nuevos que son usando el mismo número de puerto. También hay algunos trucos que implican la vinculación a puertos específicos con la opción SO_REUSEADDR, o que se cierran con la opción SO_LINGER, pero también pueden confundir conexiones antiguas y nuevas, por lo que generalmente se consideran malas ideas.

+1

Sí, eso es todo. Cambié el MSL siguiendo las instrucciones en http://www.brianp.net/2008/10/03/change-the-length-of-the-time_wait-state-on-mac-os-x/ y todo funciona ahora. ¡Gracias! – Hongli

+0

Muchas gracias, esto solucionó el mismo problema que estaba teniendo. –

+0

Aquí estaba pensando que era un problema 'Golang' .. cuando se congeló cada 16000 solicitudes de' ab' – kouton

16

En lugar de aumentar el número de puertos, cambiar la longitud de TIME_WAIT en Mac OS X.

Esto sólo funciona en el desarrollo, pero ahora puedo pedir ab para tantas peticiones como yo quiero sin tiempo de espera.

Ajuste el tiempo de espera predeterminado a 1000ms, así:

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

La página brianp.net mencionado en la otra respuesta ya no está disponible. Puede recuperarlo desde el internet archive.

Cuestiones relacionadas