Testando servidor http de alta simultaneidade

Eu estava fazendo uma pesquisa para um dos próximos projetos no meu trabalho. Eu precisava testar o servidor http se ele pode lidar com 500-1500 solicitações simultâneas.

É mais difícil fazer o teste correto do que o servidor correto. O servidor Web não tem nenhum registro no log de erros e pode processar solicitações que enviei com curl durante a execução do teste de carga.

Configuração

Primeiro eu configurei o Nginx com HttpEchoModule , usei o openresty para configurar e compilar facilmente com módulos extras.

Em seguida, crio a configuração para esperar 3 segundos e a resposta com algum texto.

Eu escolhi o nginx como exemplo para comparar com a aplicação real.

location / {
add_header
Content-Disposition "inline";
add_header
Content-Type "text/plain";

echo_sleep
3;
echo
"response text";
}

Testando

Então, começo a testar quantas solicitações ele pode processar ao mesmo tempo:

ab -n 100 -c 100 http://127.0.0.1:7080/
# => ok
ab
-n 200 -c 200 http://127.0.0.1:7080/
# => ok
ab
-n 500 -c 500 http://127.0.0.1:7080/
# => socket: Too many open files (24)

Provavelmente atingimos a limitação do sistema no máximo de arquivos abertos por processo.

Após o expediente, considero este manual mais útil
http://b.oldhu.com/2012/07/19/increase-tcp-max-connections-on-mac-os-x/

Eu também aumento worker_processese worker_connectionsno nginx:

worker_processes  2;
events
{
worker_connections
4096;
}
worker_rlimit_nofile
8000;

Depois de aumentar as limitações, tente novamente

ab -n 700 -c 700 http://127.0.0.1:7080/
# => ok
ab
-n 900 -c 900 http://127.0.0.1:7080/
# => sometimes ok
# => sometimes "apr_socket_recv: Connection reset by peer (54)"

Continue aumentando:

ab -n 1100 -c 1100 http://127.0.0.1:7080/
# => ok or apr_socket_recv error
ab
-n 3000 -c 3000 http://127.0.0.1:7080/
# => ok or apr_socket_recv error
ab
-n 9000 -c 1000 http://127.0.0.1:7080/
# => apr_socket_recv: Connection reset by peer (54)
# => Total of 455 requests completed

Assim, pude fazer 3.000 solicitações paralelas e obter uma resposta em 3.183 segundos. Mas às vezes falha com erro na abferramenta. E não pode processar quando reqs> conns. Não fiquei feliz com isso, então tento:

  • wrk – não funcionou antes, mas depois de reiniciar parece funcionar bem
  • Hellperf – Não consegui enviar tantas solicitações quanto desejo
  • cerco – estava melhor, mas ainda não conseguia fazer muitos pedidos

O que me deixa feliz é um utilitário chamado boom

Escrito em GO e fonte no github:
https://github.com/rakyll/boom

Não há binário para mac, então precisa compilar:

brew install go
export GOPATH=~/go
go
get github.com/rakyll/boom

Usando boom:

~/go/bin/boom -n 8000 -c 1500 -disable-keepalive http://127.0.0.1:7080

Tem:
* boa barra de progresso
* pode lidar com muitas solicitações (por exemplo, 30k reqs com 1500 conns)
* pode lidar com tanta simultaneidade quanto eu preciso
* continuar com erro
* tem um bom diagrama

Barra de progresso:

~/go/bin/boom -n 9000 -c 1500 -disable-keepalive http://127.0.0.1:7080
4500 / 9000 Boooooooooooooooooooooom ! 50.00 % 9s

A saída é semelhante a esta:

% ~/go/bin/boom -n 9000 -c 1500 -disable-keepalive http://127.0.0.1:7080
9000 / 9000 Booooooooooooooooooooooooooooooooooooooooooooooooooooooooo! 100.00 %

Summary:
Total: 18.6023 secs.
Slowest: 3.2399 secs.
Fastest: 2.9995 secs.
Average: 3.0652 secs.
Requests/sec: 483.8105

Status code distribution:
[200] 9000 responses

Response time histogram:
2.999 [1] |
3.024 [2383] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.048 [1883] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.072 [2011] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.096 [1219] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.120 [7] |
3.144 [188] |∎∎∎
3.168 [147] |∎∎
3.192 [361] |∎∎∎∎∎∎
3.216 [656] |∎∎∎∎∎∎∎∎∎∎∎
3.240 [144] |∎∎

Latency distribution:
10% in 3.0045 secs.
25% in 3.0219 secs.
50% in 3.0493 secs.
75% in 3.0832 secs.
90% in 3.1882 secs.
95% in 3.2031 secs.
99% in 3.2162 secs.

Aplicativo Node.js:

Em seguida, escrevo a mesma funcionalidade em Node.js:

const PORT=7090;
var reqN = 0;

var server = node.http.createServer(function (request, response) {
console
.log("Req", reqN++);
setTimeout
(function () {
response
.setHeader('content-type', 'text/plain');
response
.end("response");
}, 3000);
});

server
.listen(7090, function(){
console
.log("Server listening on: http://localhost:%s", PORT);
});

Faz o mesmo: espera 3 segundos e responde com "response"corpo.

Eu o executo com o iojs mais recente (1.7.1). O resultado do benchmark é quase o mesmo:

~/go/bin/boom -n 15000 -c 1500 -disable-keepalive http://127.0.0.1:7090
# ...
Response time histogram:
3.000 [1] |
3.039 [6930] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
3.078 [1682] |∎∎∎∎∎∎∎∎∎
3.118 [1204] |∎∎∎∎∎∎
3.157 [644] |∎∎∎
3.196 [1016] |∎∎∎∎∎
3.235 [875] |∎∎∎∎∎
3.275 [1291] |∎∎∎∎∎∎∎
3.314 [890] |∎∎∎∎∎
3.353 [262] |∎
3.393 [205] |∎

Conclusão:

Para pesquisas e desenvolvimentos futuros, prefiro usar boom , é simples, pode lidar com um grande número de solicitações paralelas e tem uma boa saída.

Nesta revisão, pulo a ferramenta chamada jMetter, porque tem GUI e fica feia no mac, também porque foi escrita em Java