Testes de Desempenho Precisos com `wrk`

API7.ai

November 25, 2022

OpenResty (NGINX + Lua)

Neste artigo, vamos falar sobre testes de desempenho. Esta parte não é exclusiva do OpenResty, mas se aplica a outros serviços de backend.

Os testes de desempenho são amplamente utilizados, e quando entregamos os produtos, eles vêm com métricas de desempenho, como QPS, TPS, latência, o número de usuários que a conexão suporta simultaneamente, e assim por diante. Para projetos de código aberto, também realizamos um teste de desempenho antes de lançar uma versão para compará-la com a anterior e ver se houve uma queda significativa. Existem também sites neutros que publicam dados comparativos de desempenho de produtos semelhantes. Devo dizer que os testes de desempenho estão próximos de nós.

Então, como realizar testes de desempenho científicos e rigorosos?

Ferramentas de Teste de Desempenho

Para fazer um bom trabalho, você deve primeiro usar uma boa ferramenta. Escolher uma boa ferramenta de teste de desempenho é metade do sucesso.

A ferramenta Apache Benchmark, também conhecida como ab, que você deve conhecer, é indiscutivelmente a ferramenta de teste de desempenho mais fácil, mas infelizmente, não é muito útil. Isso porque o lado do servidor atual é desenvolvido com base em I/O concorrente e assíncrono, cujo desempenho não é ruim. O ab não aproveita os múltiplos núcleos da máquina e as solicitações geradas não são suficientemente estressantes. Nesse caso, os resultados obtidos com o teste ab não são reais.

Portanto, podemos escolher um critério para a ferramenta de teste de estresse: a ferramenta em si tem um desempenho sólido e pode gerar estresse suficiente para pressionar o programa do lado do servidor.

Claro, você também pode gastar mais dinheiro para iniciar muitos clientes de teste de estresse e transformá-los em um sistema de teste de estresse distribuído. Mas não se esqueça de que a complexidade também aumenta.

Voltando à prática do OpenResty, nossa ferramenta de teste de desempenho recomendada é o wrk. Primeiro, por que escolhemos isso?

Primeiro, o wrk atende aos critérios de seleção de ferramentas. O estresse gerado pelo wrk em uma única máquina pode facilmente fazer com que o NGINX atinja 100% de utilização da CPU, sem mencionar outras aplicações do lado do servidor.

Segundo, o wrk tem muitas semelhanças com o OpenResty. O wrk não é um projeto de código aberto escrito do zero; ele está sobre os ombros do LuaJIT e do Redis e aproveita os recursos de múltiplos núcleos do sistema para gerar solicitações. Além disso, o wrk expõe uma API Lua que permite que você incorpore seus scripts Lua para personalizar os cabeçalhos e o conteúdo das solicitações, tornando-o muito flexível.

Então, como devemos usar o wrk? É tão simples quanto olhar o seguinte trecho de código.

wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html

Isso significa que o wrk usará 12 threads, mantendo 400 conexões longas por 30 segundos, para enviar solicitações HTTP à interface API especificada. Claro, se você não especificar parâmetros, o wrk iniciará duas threads e dez conexões longas por padrão.

Ambiente de Teste

Depois de encontrar as ferramentas de teste, não podemos iniciar o teste de estresse diretamente. Precisamos verificar o ambiente de teste uma vez. Há quatro itens principais a serem verificados no ambiente de teste, e vou discuti-los em detalhes.

1. Desative o SELinux

Se você tiver um sistema operacional CentOS/RedHat, é recomendável desativar o SELinux. Caso contrário, você pode encontrar muitos problemas de permissão estranhos.

Vamos verificar se o SELinux está ativado com o seguinte comando.

$ sestatus
SELinux status: disabled

Se ele estiver ativado (enforcing), você pode desativá-lo temporariamente com $ setenforce 0; também, modifique o arquivo /etc/selinux/config para desativá-lo permanentemente, alterando SELINUX=enforcing para SELINUX=disabled.

2. Número máximo de arquivos abertos

Em seguida, você precisa verificar o número máximo total de arquivos abertos no sistema atual com o seguinte comando.

    $ cat /proc/sys/fs/file-nr
    3984 0 3255296

O último número 3255296 aqui é o número máximo de arquivos abertos. Se esse número for pequeno em sua máquina, você precisará modificar o arquivo /etc/sysctl.conf para aumentá-lo.

fs.file-max = 1020000
net.ipv4.ip_conntrack_max = 1020000
net.ipv4.netfilter.ip_conntrack_max = 1020000

Após a modificação, você deve reiniciar o serviço do sistema para que as alterações entrem em vigor.

sudo sysctl -p /etc/sysctl.conf

3. Limites de Processo

Além do número máximo total de arquivos abertos no sistema, há também um limite para o número de arquivos que um processo pode abrir, que você pode verificar com o comando ulimit.

$ ulimit -n
1024

Você notará que esse valor padrão é 1024, um valor pequeno. Como cada solicitação do usuário corresponde a um manipulador de arquivo, e os testes de estresse geram muitas solicitações, precisamos aumentar esse valor e alterá-lo para milhões, o que você pode fazer temporariamente com o seguinte comando.

ulimit -n 1024000

Você também pode modificar o arquivo de configuração /etc/security/limits.conf para torná-lo permanente.

* hard nofile 1024000
* soft nofile 1024000

4. Configuração do NGINX

Finalmente, você precisará fazer uma pequena alteração na configuração do NGINX, que são as seguintes três linhas de código.

events {
    worker_connections 10240;
}

Isso nos permite aumentar o número de conexões por Worker. Como o valor padrão é apenas 512, isso não é suficiente para testes de alto estresse.

Verificação Antes do Teste de Estresse

Neste ponto, o ambiente de teste está pronto. Você deve estar ansioso para começar e testar, certo? Vamos verificar uma última vez antes de iniciar o teste com wrk. Afinal, as pessoas cometem erros, portanto, é essencial fazer um teste cruzado.

Este último teste pode ser dividido em duas etapas.

1. Use a ferramenta automatizada c1000k

c1000k vem do autor do SSDB. Como você pode ver pelo nome, o objetivo desta ferramenta é verificar se o seu ambiente pode atender aos requisitos de 10^6 conexões simultâneas.

O uso desta ferramenta também é bastante simples. Iniciamos um server e um client, correspondendo ao programa do servidor escutando na porta 7000 e ao programa cliente lançando o teste de estresse para simular o teste de estresse em um ambiente real:

. /server 7000
. /client 127.0.0.1 7000

Imediatamente após, o client envia uma solicitação ao server para verificar se o ambiente do sistema atual pode suportar um milhão de conexões simultâneas. Você pode executá-lo e ver o resultado.

2. Verifique se o programa do servidor está funcionando corretamente

Se o programa do lado do servidor não estiver funcionando corretamente, o teste de estresse pode se tornar um teste de atualização de log de erros ou um teste de resposta 404.

Portanto, o último e mais crucial passo do teste do ambiente de teste é executar o conjunto de testes unitários do lado do servidor ou chamar manualmente algumas interfaces principais para garantir que todas as interfaces, retornos e códigos de resposta HTTP do teste wrk estejam normais e que não haja mensagens de nível de erro em logs/error.log.

Enviando Solicitações

Ok, agora tudo está pronto para começar. Vamos iniciar o teste de estresse com wrk!

$ wrk -d 30 http://127.0.0.2:9080/hello
Running 30s test @ http://127.0.0.2:9080/hello
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   595.39us  178.51us  22.24ms   90.63%
    Req/Sec     8.33k   642.91     9.46k    59.80%
  499149 requests in 30.10s, 124.22MB read
Requests/sec:  16582.76
Transfer/sec:      4.13MB

Eu não especifiquei parâmetros aqui, então o wrk iniciará 2 threads e 10 conexões longas por padrão. Você não precisa ajustar o número de threads e conexões no wrk para ser muito grande; desde que você consiga fazer o programa alvo atingir 100% de utilização da CPU, está tudo bem.

Mas o tempo do teste de estresse não deve ser muito curto, pois alguns segundos de teste de estresse não têm significado. Caso contrário, o teste de estresse provavelmente terminará antes que o programa do servidor termine o recarregamento a quente. Ao mesmo tempo, você precisa usar uma ferramenta de monitoramento como top ou htop para verificar se o programa alvo no servidor está funcionando com 100% de utilização da CPU durante o teste de estresse.

Fenomenalmente, se a CPU estiver totalmente carregada e o uso da CPU e da memória diminuir rapidamente após o teste ser interrompido, então parabéns, o teste foi concluído com sucesso. No entanto, se houver alguma exceção como as seguintes, como desenvolvedor do lado do servidor, você deve prestar atenção.

  • A CPU não pode ser totalmente carregada. Isso não é um problema do wrk; pode ser uma limitação de rede ou uma operação de bloqueio no seu código. Você pode determinar isso revisando seu código ou usando o gráfico de chamas off CPU.
  • A CPU está sempre totalmente carregada, mesmo quando o estresse para. Isso indica um loop infinito no código causado por uma expressão regular ou um bug do LuaJIT, que eu já encontrei em ambientes reais. Neste ponto, você precisará usar o gráfico de chamas da CPU para determinar isso.

Finalmente, vamos olhar para as estatísticas do wrk. Em relação a este resultado, geralmente nos concentramos em dois valores.

O primeiro é o QPS, ou Requests/sec: 16582.76, que é um número exato que indica quantas solicitações são processadas por segundo no lado do servidor.

O segundo é a Latência: Latency 595.39us 178.51us 22.24ms 90.63%, tão importante quanto o QPS, que reflete a velocidade de resposta do sistema. Por exemplo, para aplicações de gateway, queremos manter a latência dentro de 1 ms.

Além disso, o wrk também fornece um parâmetro latency que imprime a distribuição percentual da latência em detalhes, por exemplo.

Latency Distribution
        50% 134.00us
        75% 180.00us
        90% 247.00us
        99% 552.00us

No entanto, os dados de distribuição de latência do wrk são imprecisos porque adicionam artificialmente perturbações de rede e da ferramenta que amplificam a latência, o que requer sua atenção especial.

Resumo

O teste de desempenho é um trabalho técnico; não muitas pessoas podem fazê-lo corretamente e bem. Espero que este artigo lhe dê uma compreensão mais abrangente dos testes de desempenho.

Finalmente, vou deixar uma pergunta: o wrk suporta scripts Lua personalizados para fazer testes de estresse, então você pode escrever um script Lua simples com base em sua documentação? Pode ser um pouco difícil, mas você entenderá a intenção das interfaces expostas pelo wrk quando terminar.

Você é bem-vindo para compartilhar este artigo com mais pessoas, e vamos progredir juntos.