40 votes

Le programme 'ab' se bloque après de nombreuses demandes, pourquoi?

Chaque fois que j'utilise " ab " à la référence d'un serveur web, il va geler pendant un certain temps après avoir envoyé de nombreuses demandes, uniquement pour continuer après 20 secondes.

Considérez les points suivants serveur HTTP simulateur, écrit 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

Je puis exécutez ab comme suit:

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

Pendant les premières secondes, ab fait son travail comme il est censé le faire et utilise 100% du CPU:

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

Après environ 13500 demandes, système d'utilisation de l'UC tombe à 0%. ab semble bloqué sur quelque chose. Le problème n'est pas dans le serveur, car à ce moment, le serveur appelle accept(). Après environ 20 secondes ab continue comme si de rien n'était, et utilise 100% du CPU de nouveau, seulement pour geler à nouveau au bout de quelques secondes.

Je soupçonne que quelque chose dans le noyau de la limitation des connexions, mais quoi et pourquoi? Je suis l'aide de mac OS X Leopard. J'ai vu un comportement similaire sur Linux, bien que le gel se produit à un plus grand nombre de demandes et n'arrive pas si souvent.

Ce problème m'empêche de courir grand HTTP repères.

57voto

mark4o Points 20472

Il semble que vous êtes à court de ports éphémères. Pour le vérifier, utilisez l' netstat de commandement et de chercher plusieurs milliers de ports dans l' TIME_WAIT de l'état.

Sur Mac OS X par défaut plage de ports éphémères est 49152 à 65535, pour un total de 16384 ports. Vous pouvez le vérifier avec l' sysctl commande:

$ sysctl net.inet.ip.portrange.premier net.inet.ip.portrange.dernière
net.inet.ip.portrange.première: 49152
net.inet.ip.portrange.dernier: 65535

Une fois que vous exécutez hors de ports éphémères, vous aurez normalement besoin d'attendre jusqu'à ce que l' TIME_WAIT état expire (2 * maximum segment lifetime) jusqu'à ce que vous pouvez réutiliser un numéro de port particulier. Vous pouvez doubler le nombre de ports par l'évolution de la gamme, pour démarrer à 32768, qui est la valeur par défaut sur Linux et Solaris. (Le numéro de port maximal est de 65535 si vous ne pouvez pas augmenter le haut de gamme.)

$ sudo sysctl -w net.inet.ip.portrange.tout d'abord=32768
net.inet.ip.portrange.première: 49152 -> 32768

Notez que la gamme officielle désignée par l'IANA est 49152 à 65535, et certains pare-feu peut supposer que attribuée dynamiquement les ports de l'automne à l'intérieur de cette gamme. Vous devrez peut-être reconfigurer votre pare-feu afin de rendre l'utilisation d'une gamme plus large à l'extérieur de votre réseau local.

Il est également possible de réduire au maximum segment lifetime (sysctl net.inet.tcp.msl sur Mac OS X), qui contrôle la durée de l' TIME_WAIT de l'état, mais c'est dangereux car il pourrait causer plus anciennes connexions se mêler aux plus récents qui utilisent le même numéro de port. Il y a aussi quelques astuces impliquant la liaison à des ports spécifiques avec l' SO_REUSEADDR option, ou de clôture avec l' SO_LINGER option, mais aussi celles qui pourraient causer des anciennes et de nouvelles connexions à être mélangés, de sorte sont généralement considérés comme de mauvaises idées.

26voto

Frederik Points 2936

Au lieu d'augmenter le nombre de ports, de modifier la longueur de la TIME_WAIT sur Mac OS X.

Cela ne fonctionne que dans le développement, mais je peux maintenant, demandez - ab pour un nombre de demandes que je veux sans délai.

Définir le délai d'expiration par défaut à 1000ms comme suit:

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

L'brianp.net page mentionnée dans l'autre réponse n'est plus disponible. Vous pouvez le récupérer à partir de l' internet archive.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X