Testes de performance: introduzindo carga com k6 ou JMeter

1. Fundamentos dos Testes de Performance

Testes de performance são essenciais para garantir que sistemas web e APIs suportem a carga esperada em produção. Três tipos principais se destacam:

  • Testes de carga: simulam o uso normal do sistema para verificar comportamento sob tráfego esperado.
  • Testes de estresse: aumentam progressivamente a carga até o ponto de falha, identificando limites do sistema.
  • Testes de resistência: mantêm carga constante por longos períodos para detectar vazamentos de memória ou degradação gradual.

As métricas fundamentais incluem:

  • Latência: tempo de resposta de uma requisição, medida em milissegundos.
  • Throughput: número de requisições processadas por segundo.
  • Taxa de erro: percentual de requisições que retornam códigos de erro (4xx, 5xx).
  • Percentis (p50, p95, p99): distribuição de latência — p99 indica que 99% das requisições são mais rápidas que aquele valor.

Realizar testes antes de ir para produção evita surpresas como lentidão generalizada, timeouts e perda de receita durante picos de acesso.

2. JMeter: A Ferramenta Clássica e Robusta

JMeter é uma ferramenta madura baseada em Java, amplamente adotada por sua interface gráfica e vasto ecossistema de plugins.

Arquitetura básica

Um plano de teste no JMeter contém:

  • Thread Group: define número de usuários virtuais, ramp-up e duração.
  • Samplers: realizam requisições HTTP, JDBC, SOAP, etc.
  • Listeners: exibem resultados em tabelas, gráficos e relatórios.

Exemplo prático: script de carga simples

Crie um plano de teste com as seguintes configurações:

Thread Group:
  - Número de threads (usuários): 50
  - Período de ramp-up (segundos): 10
  - Contagem de loops: 10

HTTP Request Defaults:
  - Protocolo: https
  - Servidor: api.exemplo.com
  - Porta: 443

HTTP Request:
  - Método: GET
  - Path: /v1/produtos

Listener: Summary Report
  - Salvar resultados em arquivo CSV

Execute em modo headless para produção:

jmeter -n -t script.jmx -l resultados.csv -e -o relatorio/

Vantagens e limitações

Vantagens: interface gráfica intuitiva, suporte a múltiplos protocolos (HTTP, JDBC, FTP, JMS), relatórios integrados.

Limitações: consumo elevado de memória para grandes cargas, scripts em XML (difícil versionamento), curva de aprendizado para cenários complexos.

3. k6: Modernidade e Código como Script

k6 é uma ferramenta open-source escrita em Go, com scripts em JavaScript, projetada para integração contínua e pipelines DevOps.

Conceitos fundamentais

  • VUs (Virtual Users): cada VU executa o script de forma independente.
  • Stages: definem como a carga varia ao longo do tempo (ramp-up, steady, ramp-down).
  • Checks: validações de resposta (status code, tempo máximo).

Exemplo prático: teste de carga com k6

Crie um arquivo teste-carga.js:

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 20 },  // ramp-up para 20 VUs
    { duration: '1m', target: 20 },   // carga constante
    { duration: '30s', target: 0 },   // ramp-down
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% das requisições < 500ms
    http_req_failed: ['rate<0.05'],   // taxa de erro < 5%
  },
};

export default function () {
  const res = http.get('https://api.exemplo.com/v1/produtos');

  check(res, {
    'status code é 200': (r) => r.status === 200,
    'tempo de resposta < 300ms': (r) => r.timings.duration < 300,
  });

  sleep(1); // pausa de 1 segundo entre requisições
}

Execute localmente:

k6 run teste-carga.js

Para gerar relatório HTML:

k6 run teste-carga.js --out json=resultados.json
k6 convert resultados.json --out html > relatorio.html

Integração CI/CD

k6 pode ser executado em pipelines GitHub Actions, GitLab CI ou Jenkins sem interface gráfica:

# Exemplo de comando em pipeline
k6 run --vus 50 --duration 60s teste-carga.js

4. Comparação Prática: JMeter vs. k6

Aspecto JMeter k6
Curva de aprendizado Moderada (interface gráfica) Baixa (JavaScript)
Consumo de recursos Alto (Java VM) Baixo (Go runtime)
Execução distribuída Nativa (Master-Slave) k6 Cloud ou Kubernetes
Versionamento de scripts Difícil (XML) Fácil (JS em Git)
Protocolos suportados HTTP, JDBC, JMS, FTP, SOAP HTTP/1.1, HTTP/2, gRPC, WebSocket
Relatórios visuais Integrados (Listener) k6 Cloud ou grafana/k6

Quando usar JMeter: equipes com forte tradição em QA, necessidade de protocolos legados (JDBC, JMS), cenários com interface gráfica para stakeholders não-técnicos.

Quando usar k6: pipelines DevOps, times de desenvolvimento que preferem código, testes de APIs REST e gRPC, ambientes com recursos limitados.

Usar ambas: JMeter para testes de aceitação manuais e k6 para testes automatizados em CI/CD.

5. Planejando um Cenário de Carga Realista

Definindo objetivos

  • Pico esperado: 1000 requisições/segundo durante 10 minutos.
  • SLA: p95 < 500ms, taxa de erro < 1%.
  • Limites aceitáveis: degradação gradual, sem falhas catastróficas.

Modelagem de usuários

Stages recomendados:
1. Ramp-up: 0 → 100 VUs em 2 minutos
2. Steady: 100 VUs por 5 minutos
3. Ramp-down: 100 → 0 VUs em 2 minutos

Dados de teste

Evite efeitos de cache utilizando:

  • Parametrização: dados diferentes para cada VU (tokens, IDs de usuário).
  • Headers aleatórios: User-Agent, Accept-Language.
  • Dados realistas: use datasets com CSV ou JSON.

Exemplo com k6:

import { SharedArray } from 'k6/data';

const dados = new SharedArray('usuarios', function () {
  return JSON.parse(open('./usuarios.json'));
});

export default function () {
  const usuario = dados[Math.floor(Math.random() * dados.length)];
  // usa usuario.token e usuario.id na requisição
}

6. Analisando Resultados e Identificando Gargalos

Leitura de relatórios

Com k6, analise o resumo final:

✓ status code é 200
✓ tempo de resposta < 300ms

http_req_duration........: avg=245ms  min=120ms  med=230ms  max=890ms
http_req_duration........: p(90)=380ms  p(95)=450ms  p(99)=720ms
http_req_failed..........: 2.3% ✓ 23   ✗ 977
http_reqs................: 1000 16.67/s

Sinais de alerta:

  • p99 muito acima de p95: picos de latência indicam contenção de recursos.
  • Taxa de erro crescente durante steady state: degradação sob carga.
  • Throughput constante mesmo aumentando VUs: limite do sistema.

Correlação com infraestrutura

Combine métricas de performance com:

  • APM (Application Performance Monitoring): New Relic, Datadog.
  • Logs do servidor: Nginx, aplicação (ELK Stack).
  • Métricas de infra: CPU, memória, I/O de disco e rede.

Estratégias de otimização:

  1. Código: otimizar consultas N+1, usar cache local.
  2. Banco de dados: índices faltantes, queries lentas.
  3. Cache: Redis, CDN para conteúdo estático.
  4. Escalabilidade: horizontal (mais instâncias) ou vertical (mais recursos).

7. Boas Práticas e Armadilhas Comuns

Armadilhas a evitar

  • Testar em produção sem isolamento: pode degradar experiência de usuários reais.
  • Ignorar warm-up: sistemas com cache a frio apresentam latência maior no início.
  • Dados estáticos: todos os VUs acessando o mesmo recurso geram resultados irreais.

Boas práticas

  1. Versionamento de scripts: mantenha scripts em Git com o código da aplicação.
  2. Repetibilidade: use sementes fixas para dados aleatórios.
  3. Ambiente isolado: réplica de produção com dados anonimizados.
  4. Monitoramento contínuo: execute testes a cada deploy.

Quando usar cada ferramenta

Cenário Ferramenta recomendada
Teste rápido de API REST k6
Teste de banco de dados (JDBC) JMeter
Pipeline CI/CD k6
Relatório visual para stakeholders JMeter
Teste de WebSocket em tempo real k6
Protocolo legado (FTP, JMS) JMeter

Referências