Você lançou um endpoint. Ele funciona no seu navegador. Mas você não tem ideia do que acontece quando 400 pessoas o acessam ao mesmo tempo. A latência permanece estável ou o nonagésimo nono percentil dispara? O servidor aguenta 1.000 requisições por segundo, ou ele falha com 300?
O wrk responde a isso. É uma pequena ferramenta de linha de comando que dispara muito tráfego HTTP para uma URL e relata a velocidade com que o servidor respondeu sob essa carga.
O que é o wrk e quando usá-lo
wrk é uma ferramenta moderna de benchmark HTTP. Ela gera carga a partir de uma única máquina multi-core e mede a latência e a taxa de requisições que o servidor retorna. Ela usa multithreading mais um loop de eventos escalável (epoll no Linux, kqueue no macOS), então uma única instância pode gerar muito tráfego sem a necessidade de uma frota de máquinas de carga.
Use o wrk quando você quiser números de desempenho brutos:
- Quantas requisições por segundo este endpoint pode sustentar?
- Como a latência se comporta na mediana versus na cauda?
- O servidor se mantém em uma janela sustentada, ou ele degrada após um minuto?
O wrk é uma ferramenta de benchmark, não um conjunto de testes. Ele mede a velocidade. Ele não verifica se o corpo JSON está correto, se o código de status é 200, ou se o contrato da API foi mantido. Tenha essa distinção em mente. Voltaremos a ela perto do final, porque ela muda a forma como você encaixa o wrk em um fluxo de trabalho de teste real. Se você quiser uma visão mais ampla primeiro, este guia de teste de carga de API aborda os conceitos que o wrk coloca em prática.
Instalando o wrk
macOS
O Homebrew possui um binário pré-compilado, que é o caminho de menor resistência:
brew install wrk
No Apple Silicon, isso importa. A compilação a partir do código-fonte pode encontrar problemas com LuaJIT ARM64, então o binário do Homebrew te poupa dessa dor de cabeça.
Linux (compilar do código-fonte)
Não há um pacote apt oficial, então você precisa compilá-lo. Instale o toolchain e os cabeçalhos OpenSSL primeiro:
sudo apt-get install build-essential libssl-dev git -y
Em seguida, clone e compile:
git clone https://github.com/wg/wrk.git
cd wrk
make
Isso produz um binário `wrk` no diretório atual. Mova-o para o seu PATH para que você possa chamá-lo de qualquer lugar:
sudo cp wrk /usr/local/bin
Confirme se ele funciona:
wrk --version
O Comando Básico
Aqui está a estrutura de cada execução do wrk:
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
Quatro coisas estão acontecendo. Vamos detalhar as flags.
-t, --threadsdefine o número de threads do SO que o wrk irá iniciar. Um bom ponto de partida é uma thread por núcleo de CPU. Aqui são 12.-c, --connectionsdefine o total de conexões HTTP mantidas abertas em todas as threads. Aqui, 400 conexões são distribuídas pelas 12 threads. Conexões são como você simula clientes concorrentes.-d, --durationdefine por quanto tempo o teste será executado. Aceita valores como30s,2mou2h. Aqui são 30 segundos.
Mais duas flags que você usará constantemente:
--latencyimprime um detalhado detalhamento percentil de latência. Ative isso quase todas as vezes. As médias escondem a latência de cauda, e a cauda é geralmente o que prejudica os usuários.--timeoutregistra uma requisição como expirada se nenhuma resposta chegar dentro da janela que você definiu, por exemplo--timeout 2s. Sem isso, respostas lentas podem distorcer seus números de latência.
Uma execução que você usará frequentemente se parece com isto:
wrk -t8 -c200 -d30s --latency http://localhost:3000/api/users
Oito threads, 200 conexões, 30 segundos, com a distribuição completa da latência impressa no final.
Lendo a Saída
O wrk imprime um relatório compacto. Aqui está uma execução real contra um pequeno serviço:
Running 5s test @ http://10.135.232.163:3000
2 threads and 5 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 3.82ms 2.64ms 26.68ms 85.81%
Req/Sec 550.90 202.40 0.98k 68.00%
5494 requests in 5.01s, 1.05MB read
Requests/sec: 1096.54
Transfer/sec: 215.24KB
Leia de baixo para cima, porque as duas últimas linhas são a manchete.
Requests/sec é a taxa de transferência: quantas requisições o servidor concluiu por segundo em média. Aqui são 1.096. Este é o número que você compara entre as execuções e as mudanças de código.
Transfer/sec é a largura de banda: quantos dados foram transferidos por segundo. Útil quando os payloads são grandes ou você suspeita que está limitado pela largura de banda em vez da CPU.
Agora, a tabela Thread Stats, que descreve a distribuição, não apenas a média:
- Linha de Latência: a latência média foi de 3.82ms, mas o desvio padrão foi de 2.64ms e o máximo atingiu 26.68ms. Um máximo alto em relação à média é um sinal. Algumas requisições são lentas, mesmo que a maioria seja rápida.
- Linha de Req/Sec: a taxa de requisições por thread, novamente com sua dispersão.
A coluna +/- Stdev informa a porcentagem de amostras que caíram dentro de um desvio padrão. Porcentagens menores significam uma dispersão maior e menos previsível.
A linha 5494 requests in 5.01s confirma o volume total que a execução realmente impulsionou.
Quando você adiciona --latency, o wrk imprime um bloco de percentis para que você possa ver a cauda diretamente:
Latency Distribution
50% 3.21ms
75% 4.86ms
90% 7.09ms
99% 14.13ms
O 99º percentil é o número a ser observado. Se 99% das requisições terminam em 14ms, mas sua média é de 3.82ms, um em cada cem usuários está esperando muito mais tempo do que a média sugere. As médias mentem sobre as caudas. Os percentis não.
Enviando Requisições POST e Cabeçalhos Personalizados com um Script Lua
Por padrão, o wrk envia requisições GET. Para enviar um POST, adicionar um corpo ou definir cabeçalhos personalizados, você passa um script Lua com -s.
Crie um arquivo chamado post.lua:
wrk.method = "POST"
wrk.body = '{"name": "Ada", "role": "engineer"}'
wrk.headers["Content-Type"] = "application/json"
Três campos fazem o trabalho. wrk.method define o verbo HTTP. wrk.body define o corpo da requisição. wrk.headers é uma tabela onde cada chave é um nome de cabeçalho.
Execute-o apontando -s para o script:
wrk -t4 -c100 -d30s -s post.lua --latency http://localhost:3000/api/users
Para um POST com codificação de formulário em vez de JSON, o repositório do wrk envia este exato exemplo:
wrk.method = "POST"
wrk.body = "foo=bar&baz=quux"
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
Você também pode definir cabeçalhos com a flag -H para casos mais simples, sem um script:
wrk -t4 -c100 -d30s -H "Authorization: Bearer TOKEN123" --latency http://localhost:3000/api/protected
Use -H para um ou dois cabeçalhos. Use um script Lua quando precisar de um corpo, um método não-GET, ou lógica por requisição.
Os Limites: wrk Não Verifica a Correção
Aqui está a parte que as pessoas perdem. O wrk te diz a velocidade com que o servidor respondeu. Ele não te diz se a resposta estava correta.
Aponte o wrk para um endpoint que retorna HTTP 500 em cada requisição, e você obterá um relatório com boa aparência e um alto número de requisições por segundo. O wrk conta uma troca HTTP completa. Ele não verifica o código de status, não valida o corpo da resposta contra um esquema, ou confirma se a API fez o que deveria. Erros podem até parecer rápidos, porque um servidor que rejeita requisições cedo faz menos trabalho por requisição.
Então, o wrk responde "é rápido o suficiente sob carga?". Ele não pode responder "está correto?". Ambas as perguntas importam, e elas precisam de ferramentas diferentes. Um número de carga em um endpoint quebrado é um número em que você não deve confiar. É exatamente por isso que as equipes combinam uma ferramenta de benchmark com um conjunto de testes funcionais. Uma prova a velocidade. A outra prova o comportamento.
Onde o Apidog e os Testes Funcionais se Encaixam
O fluxo de trabalho limpo tem duas camadas, executadas em ordem.
Primeiro, valide o comportamento. Antes de se preocupar com a velocidade de um endpoint, confirme se ele está correto. No Apidog, você constrói cenários de teste que enviam requisições reais e verificam o que retorna: códigos de status, campos JSON, esquema de resposta e lógica de negócios. Você pode encadear requisições, passar dados entre etapas e executar o mesmo cenário em diferentes ambientes. Esta é a camada que detecta o erro 500 que o wrk felizmente benchmarka.
Em seguida, faça o benchmark da taxa de transferência. Uma vez que o comportamento é verificado, execute o wrk contra os mesmos endpoints para ver como eles se comportam sob concorrência e carga sustentada. O Apidog também possui testes de desempenho integrados se você preferir manter o trabalho funcional e de carga em um só lugar, mas o wrk é uma excelente ferramenta dedicada para benchmarks brutos de linha de comando.
A camada funcional é executada na CI, não apenas no seu laptop. A CLI do Apidog é headless, então ela se encaixa em qualquer etapa do pipeline que possa executar Node. Instale-o:
npm install -g apidog-cli
Em seguida, execute um cenário de teste ou suite salvo por ID:
apidog run \
--access-token "$APIDOG_ACCESS_TOKEN" \
-t <scenarioOrSuiteId> \
-e <environmentId> \
-r cli,html,junit
-t é o ID do cenário, pasta ou suite a ser executado. -e é o ID do ambiente. -r escolhe os formatos de relatório, um ou mais de cli, html, json e junit. A saída JUnit se conecta diretamente à maioria dos sistemas de CI para validação de aprovação/falha. Para execuções orientadas por dados, adicione -d (ou --iteration-data) com um caminho de arquivo ou ID de dados de teste para iterar o mesmo cenário em muitas linhas de entrada.
A CLI executa cenários e suítes Apidog salvos. É headless, não um enviador de requisições interativo, e não é um gerador de carga. É o portão de correção. O wrk é o medidor de velocidade. Execute o portão de correção em seu pipeline (veja este tutorial de CI/CD da CLI do Apidog ou o guia do GitHub Actions para configuração de copiar e colar), então faça o benchmark com o wrk quando precisar dos números de throughput. A referência completa da CLI cobre o restante das flags.
FAQ
Qual a diferença entre wrk e ab (ApacheBench)? Ambos disparam carga HTTP e reportam requisições por segundo. O wrk é multi-threaded e usa um loop de eventos, então ele gera mais carga de uma única máquina e lida melhor com alta concorrência. O ab é single-threaded. Para cargas pesadas de uma única máquina, o wrk geralmente escala mais. Nenhum deles verifica a correção da resposta.
Quantas threads e conexões devo usar? Comece com uma thread por núcleo de CPU e defina as conexões para o nível de concorrência que você deseja simular. Se você tem 8 núcleos e deseja modelar 200 clientes concorrentes, tente -t8 -c200. Observe a máquina cliente. Se o próprio wrk estiver limitado pela CPU, seus números refletem o limite do gerador de carga, não do servidor. Aumente as conexões até que a taxa de transferência pare de aumentar.
O wrk pode testar endpoints HTTPS? Sim. Aponte-o para uma URL https:// e o wrk lida com TLS. É por isso que a compilação para Linux precisa de libssl-dev. Os handshakes TLS adicionam custo de CPU em ambas as extremidades, então espere uma taxa de transferência bruta menor contra HTTPS do que HTTP puro.
O wrk valida o corpo da resposta ou o código de status? Não. O wrk conta as trocas HTTP concluídas e mede o tempo. Ele não verifica códigos de status ou corpos, então um endpoint que retorna erros ainda pode apresentar um alto número de requisições por segundo. Use um conjunto de testes funcionais, como um executado através da CLI do Apidog, para verificar a correção, e então use o wrk para a taxa de transferência.
Por quanto tempo deve ser executado um teste de carga? Tempo suficiente para superar efeitos de aquecimento, como caches frios e compilação JIT. Alguns segundos são suficientes para uma verificação rápida, mas 30 segundos a alguns minutos fornecem números mais estáveis e revelam degradação que só aparece sob carga sustentada. Use -d30s como um padrão sensato e aumente-o quando estiver perseguindo vazamentos lentos.
