'Ab' congela programa após muitas solicitações, por quê?
-
06-07-2019 - |
Pergunta
Sempre que eu uso 'ab' a referência de um servidor web, ele irá congelar por um tempo depois de ter lotes enviados de pedidos, apenas para continuar depois de 20 segundos ou assim.
Considere o seguinte simulador de servidor HTTP, escrito em 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
Eu, então, executar ab da seguinte forma:
ab -n 45000 -c 10 http://127.0.0.1:3000/
Durante os primeiros segundos, ab faz o seu trabalho, uma vez que é suposto e usa 100% da CPU:
Benchmarking 127.0.0.1 (be patient)
Completed 4500 requests
Completed 9000 requests
Completed 13500 requests
Depois de cerca de 13500 pedidos, o uso da CPU do sistema cai para 0%. ab parece ser congelados em algo. O problema não está no servidor, porque, neste momento, o servidor está chamando accept (). Após cerca de 20 segundos ab continua como se nada tivesse acontecido, e vai usar 100% da CPU novamente, só para congelar novamente após alguns segundos.
Eu suspeito algo no kernel está estrangulando conexões, mas o que e por quê? Estou usando OS X Leopard. Eu vi um comportamento semelhante em Linux, bem como, embora o congelamento acontece em um número muito maior de pedidos e não acontece com tanta frequência.
Este problema impede-me de correr benchmarks grande HTTP.
Solução
Parece que você está funcionando fora do efémero portas . Para verificar, use o href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man1/netstat.1.html" rel="noreferrer"> netstat
comando e olhar por vários milhares de portas na TIME_WAIT
estado
No Mac OS X, o intervalo de portas efêmeras padrão é 49152-65535, para um total de 16384 portas. Você pode verificar isso com o href="http://developer.apple.com/documentation/Darwin/Reference/ManPages/man8/sysctl.8.html" rel="noreferrer"> sysctl
comando :
$ sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last net.inet.ip.portrange.first: 49152 net.inet.ip.portrange.last: 65535
Uma vez que você correr para fora de portas efêmeras, normalmente você vai precisar esperar até que o estado TIME_WAIT
expira (2 * tempo de vida máximo de segmento) até que você pode reutilizar um número de porta específico. Você pode dobrar o número de portas, alterando o intervalo para começar em 32768, que é o padrão no Linux e Solaris. (O número da porta máxima é de 65535 para que você não pode aumentar a parte alta.)
$ sudo sysctl -w net.inet.ip.portrange.first=32768 net.inet.ip.portrange.first: 49152 -> 32768
Note que a gama oficial designado pela IANA é 49152-65535, e alguns firewalls pode-se supor que as portas atribuídas dinamicamente cair dentro desse intervalo. Você pode precisar reconfigurar o seu firewall, a fim de fazer uso de um fora da faixa maior de sua rede local.
Também é possível reduzir o tempo de vida máximo de segmento (sysctl net.inet.tcp.msl
no Mac OS X), que controla a duração do estado TIME_WAIT
, mas isso é perigoso, pois pode causar conexões mais velhos para se misturam com os mais novos que estão usando o mesmo número de porta. Existem também alguns truques que envolvem ligação a portas específicas com a opção SO_REUSEADDR
, ou fechar com a opção SO_LINGER
, mas aqueles também poderia causar velho e novas conexões a serem misturados, por isso são geralmente consideradas como más idéias.
Outras dicas
Em vez de aumentar o número de portas, alterar o comprimento de TIME_WAIT
no Mac OS X.
Isso só funciona em desenvolvimento, mas agora eu posso pedir ab
para tantos pedidos como eu quero, sem tempo limite.
Definir o tempo limite padrão de 1000ms assim:
$ sudo sysctl -w net.inet.tcp.msl=1000
net.inet.tcp.msl: 15000 -> 1000
A página brianp.net mencionado na outra resposta não está mais disponível. Você pode recuperá-lo a partir do arquivo de internet .
Outra opção para resolver o problema é habilitar HTTP KeepAlive adicionando a opção "-k"
. Isso fará com que ab para reutilizar as conexões TCP e, como consequência, não irá esgotar todas as portas disponíveis. Por exemplo:
ab -n 45000 -c 10 -k http://127.0.0.1:3000/