Parte da nossa série Performance & Scalability
Leia o guia completoTeste de carga k6: teste de resistência de suas APIs antes do lançamento
Enviar um produto sem teste de carga é uma aposta. Você não sabe qual é o seu ponto de ruptura até que os usuários o encontrem para você – geralmente durante o lançamento de um produto, um momento viral ou um pico de vendas. k6 é a ferramenta moderna de teste de carga que permite escrever testes em JavaScript, executá-los a partir de CI e descobrir seu limite de desempenho antes dos usuários. É amigável ao desenvolvedor, eficiente em termos de recursos (k6 usa goroutines, não threads) e integra-se perfeitamente com Grafana e Prometheus para métricas em tempo real.
Este guia cobre o k6 desde o primeiro script até testes de carga complexos em vários cenários, métricas personalizadas, limites, integração de CI e padrões de teste seguros para produção para APIs Node.js/NestJS.
Principais conclusões
- Os scripts k6 são JavaScript, mas são executados em um tempo de execução Go — sem APIs Node.js (sem
require, semfs, semsetTimeout)- Usuários Virtuais (VUs) são usuários simulados simultâneos; iterações são execuções de script individuais
- Sempre defina limites antes de executar — limites com falha interrompem o teste e falham no CI
- Usar cenários (vus constante, vus em rampa, taxa de chegada constante) para modelar padrões de tráfego reais
- Nunca carregue a produção de teste sem coordenação com as operações — teste em relação à preparação ou a um clone de produção
- A métrica
http_req_durationé sua principal métrica de SLA — p95 e p99 são mais importantes que as médias- Limite de taxa para seus corredores k6 – o tráfego de teste de carga não deve esgotar seu orçamento de limite de taxa
- Use k6 Cloud ou Grafana k6 para testes de carga distribuídos em regiões geográficas
Instalação e primeiro script
# macOS
brew install k6
# Ubuntu/Debian
sudo gpg -k && sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
# Windows (via Chocolatey)
choco install k6
Seu primeiro script k6:
// k6/homepage-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate } from 'k6/metrics';
// Custom metric: error rate
const errorRate = new Rate('error_rate');
export const options = {
// Ramp up to 50 VUs over 1 minute, hold 2 minutes, ramp down
stages: [
{ duration: '1m', target: 50 },
{ duration: '2m', target: 50 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95th pct < 500ms
http_req_failed: ['rate<0.01'], // Less than 1% errors
error_rate: ['rate<0.01'],
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';
export default function () {
const res = http.get(`${BASE_URL}/`);
const ok = check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body contains ECOSIRE': (r) => r.body.includes('ECOSIRE'),
});
errorRate.add(!ok);
sleep(1); // 1 second think time between iterations
}
Execute:
k6 run k6/homepage-load.js
k6 run --env BASE_URL=https://staging.ecosire.com k6/homepage-load.js
k6 run --vus 100 --duration 30s k6/homepage-load.js # Quick smoke test
Teste de carga de API com autenticação
// k6/api-load.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Counter, Trend } from 'k6/metrics';
const apiCalls = new Counter('api_calls');
const authDuration = new Trend('auth_duration');
export const options = {
scenarios: {
// Constant arrival rate: 100 requests/second regardless of VU count
constant_load: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '3m',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
thresholds: {
http_req_duration: ['p(95)<300', 'p(99)<500'],
'http_req_duration{type:api}': ['p(95)<200'],
http_req_failed: ['rate<0.005'], // 0.5% error budget
},
};
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3001';
// setup() runs once before VUs start
export function setup() {
// Authenticate and return tokens for VUs to use
const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: __ENV.TEST_EMAIL,
password: __ENV.TEST_PASSWORD,
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, { 'login successful': (r) => r.status === 200 });
const { accessToken } = loginRes.json();
return { accessToken };
}
export default function (data) {
const { accessToken } = data;
const headers = {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
};
group('Contacts API', () => {
// List contacts
const listRes = http.get(`${BASE_URL}/contacts?page=1&limit=20`, {
headers,
tags: { type: 'api', endpoint: 'contacts-list' },
});
check(listRes, {
'list contacts: 200': (r) => r.status === 200,
'list contacts: has items': (r) => r.json('data') !== null,
});
apiCalls.add(1);
sleep(0.5);
// Create a contact
const createRes = http.post(`${BASE_URL}/contacts`, JSON.stringify({
name: `Load Test User ${Date.now()}`,
email: `load-test-${Date.now()}@example.com`,
}), {
headers,
tags: { type: 'api', endpoint: 'contacts-create' },
});
check(createRes, {
'create contact: 201': (r) => r.status === 201,
});
apiCalls.add(1);
});
sleep(1);
}
// teardown() runs once after all VUs finish
export function teardown(data) {
// Clean up test data if needed
console.log(`Test complete. Data: ${JSON.stringify(data)}`);
}
Cenários VU de aceleração
Modele padrões de tráfego reais com vários estágios e cenários:
// k6/stress-test.js
export const options = {
scenarios: {
// Scenario 1: Normal traffic baseline
normal_traffic: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 20 },
{ duration: '5m', target: 20 },
],
gracefulRampDown: '30s',
},
// Scenario 2: Sudden spike (marketing campaign goes live)
traffic_spike: {
executor: 'ramping-vus',
startVUs: 0,
startTime: '7m', // Starts after baseline is stable
stages: [
{ duration: '30s', target: 200 }, // Rapid spike
{ duration: '2m', target: 200 }, // Hold spike
{ duration: '30s', target: 20 }, // Fall back
],
},
// Scenario 3: Soak test for memory leaks
soak_test: {
executor: 'constant-vus',
vus: 10,
duration: '30m', // Run for 30 minutes
startTime: '10m',
},
},
thresholds: {
'http_req_duration{scenario:normal_traffic}': ['p(95)<300'],
'http_req_duration{scenario:traffic_spike}': ['p(95)<800'],
http_req_failed: ['rate<0.02'],
},
};
Teste de rastreamento de blog/sitemap
// k6/sitemap-crawl.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { SharedArray } from 'k6/data';
// Load URLs from sitemap (pre-fetched and saved as JSON)
const urls = new SharedArray('sitemap urls', function () {
// In real usage, fetch sitemap XML and parse URLs beforehand
// SharedArray is loaded once and shared across all VUs (memory efficient)
return JSON.parse(open('./sitemap-urls.json'));
});
export const options = {
vus: 20,
duration: '5m',
thresholds: {
http_req_duration: ['p(95)<2000'], // Public pages can be slower
http_req_failed: ['rate<0.01'],
},
};
export default function () {
// Each VU picks a random URL from the sitemap
const url = urls[Math.floor(Math.random() * urls.length)];
const res = http.get(url, {
headers: {
// Identify as load tester in logs
'User-Agent': 'k6-load-tester/1.0',
},
timeout: '10s',
});
check(res, {
'status is 200': (r) => r.status === 200,
'no error page': (r) => !r.body.includes('Error'),
'has canonical tag': (r) => r.body.includes('rel="canonical"'),
'response time < 2000ms': (r) => r.timings.duration < 2000,
});
sleep(Math.random() * 2 + 1); // Random 1-3 second think time
}
Métricas e limites personalizados
// k6/checkout-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Counter, Rate, Trend, Gauge } from 'k6/metrics';
// Custom metrics for business-specific tracking
const checkoutSuccesses = new Counter('checkout_successes');
const checkoutFailures = new Counter('checkout_failures');
const checkoutDuration = new Trend('checkout_duration');
const activeCheckouts = new Gauge('active_checkouts');
export const options = {
stages: [
{ duration: '1m', target: 10 },
{ duration: '3m', target: 30 },
{ duration: '1m', target: 0 },
],
thresholds: {
checkout_duration: ['p(95)<3000'], // Checkout < 3s at p95
checkout_failures: ['count<10'], // Max 10 absolute failures
http_req_duration: ['p(99)<2000'],
},
};
export default function (data) {
activeCheckouts.add(1);
const start = Date.now();
// Step 1: Add to cart
const cartRes = http.post('/api/cart/add', JSON.stringify({
productId: 'prod_odoo_customization',
quantity: 1,
}), { headers: { 'Content-Type': 'application/json' } });
if (!check(cartRes, { 'add to cart: 200': (r) => r.status === 200 })) {
checkoutFailures.add(1);
activeCheckouts.add(-1);
return;
}
sleep(2);
// Step 2: Create checkout session
const checkoutRes = http.post('/api/billing/checkout', JSON.stringify({
cartId: cartRes.json('cartId'),
}), { headers: { 'Content-Type': 'application/json' } });
const success = check(checkoutRes, {
'checkout session created': (r) => r.status === 200,
'has session URL': (r) => r.json('url') !== null,
});
if (success) {
checkoutSuccesses.add(1);
checkoutDuration.add(Date.now() - start);
} else {
checkoutFailures.add(1);
}
activeCheckouts.add(-1);
sleep(1);
}
Integração de saída Grafana
Transmita métricas k6 para o InfluxDB e visualize no Grafana durante o teste:
# Run with InfluxDB output
k6 run --out influxdb=http://localhost:8086/k6 k6/api-load.js
# Or use the k6 Grafana integration (k6 v0.45+)
k6 run --out experimental-prometheus-rw k6/api-load.js
# docker-compose.monitoring.yml
services:
influxdb:
image: influxdb:2.7
ports: ["8086:8086"]
environment:
INFLUXDB_DB: k6
INFLUXDB_ADMIN_USER: admin
INFLUXDB_ADMIN_PASSWORD: password
grafana:
image: grafana/grafana:latest
ports: ["3030:3000"]
environment:
GF_AUTH_ANONYMOUS_ENABLED: "true"
volumes:
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
CI Integration
# .github/workflows/ci.yml (load test section)
load-tests:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # Only on main branch
needs: [deploy-staging]
steps:
- uses: actions/checkout@v4
- name: Install k6
run: |
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
| sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
- name: Run smoke test (30 VUs, 60s)
run: |
k6 run --vus 30 --duration 60s \
--env BASE_URL=${{ secrets.STAGING_URL }} \
--env TEST_EMAIL=${{ secrets.TEST_EMAIL }} \
--env TEST_PASSWORD=${{ secrets.TEST_PASSWORD }} \
apps/api/k6/api-load.js
- name: Upload k6 results
uses: actions/upload-artifact@v4
if: always()
with:
name: k6-results
path: k6-results/
Linhas de base de desempenho
Estabeleça linhas de base e alerte sobre regressões:
| Ponto final | p50 | p95 | p99 | Erro no orçamento |
|---|---|---|---|---|
GET / (página inicial) | 120ms | 400ms | 800ms | 0,1% |
| CÓDIGO0 | 50ms | 150ms | 300ms | 0,5% |
| CÓDIGO0 | 80ms | 200ms | 400ms | 0,5% |
| CÓDIGO0 | 200ms | 500ms | 1000ms | 1% |
| CÓDIGO0 | 500ms | 1500ms | 3000ms | 1% |
| CÓDIGO0 | 100ms | 300ms | 600ms | 0,1% |
Perguntas frequentes
Qual é a diferença entre VUs e solicitações por segundo?
VUs (Usuários Virtuais) são usuários simulados simultâneos. Cada VU executa sua função padrão em um loop. As solicitações por segundo dependem da rapidez com que cada iteração é executada. Se cada iteração da VU levar 2 segundos (incluindo o tempo de suspensão) e você tiver 100 VUs, obterá aproximadamente 50 RPS. Use o executor constant-arrival-rate quando precisar direcionar um RPS específico, independentemente da duração da iteração.
Devo carregar o teste em produção ou teste?
Prefira sempre a encenação. Os testes de carga de produção correm o risco de impactar usuários reais, esgotar os limites de taxa, criar pedidos reais a partir de dados de teste e acionar eventos reais de webhook. Se você precisar testar a produção, faça-o durante janelas de baixo tráfego, use tokens de pagamento de teste e tenha um plano de reversão. O k6 Cloud oferece suporte à geração de carga distribuída geograficamente para que você possa testar o desempenho da borda do CDN sem tocar na origem.
Como lidar com a expiração do token de autenticação durante testes longos?
Para testes curtos (menos de 15 minutos), obtenha um token em setup() e passe-o para todas as VUs via data. Para testes longos (mais de 15 minutos), implemente a atualização do token no script de teste: verifique a idade do token na função padrão e atualize quando ele expirar. Armazene o token em uma variável JavaScript local para cada VU.
Qual é a diferença entre stages e scenarios?
stages é uma abreviação para um único cenário ramping-vus – bom para padrões simples de aceleração/manutenção/desaceleração. scenarios oferece controle total: vários padrões de tráfego simultâneos, diferentes executores por cenário, limites por cenário e marcação de cenário em métricas. Use scenarios para testes realistas de vários padrões (linha de base + pico + absorção simultaneamente).
Quantas VUs um único processo k6 pode manipular?
Um único processo k6 em hardware moderno pode sustentar de 5.000 a 10.000 VUs e gerar de 50.000 a 100.000 RPS, dependendo da complexidade do script e do tamanho da resposta. Para cargas mais altas, use k6 cloud ou execute várias instâncias k6 atrás de um distribuidor de carga. Cada VU é extremamente leve (uma goroutine) — k6 usa muito menos recursos que JMeter ou Gatling para contagens de VU equivalentes.
Próximas etapas
O teste de carga revela o verdadeiro perfil de desempenho do seu aplicativo antes que os usuários o descubram sob estresse. Os padrões neste guia (cenários de rampa, limites personalizados, integração de CI e painéis Grafana) fornecem a infraestrutura para encontrar e corrigir gargalos de desempenho continuamente.
ECOSIRE cria APIs NestJS com desempenho validado com testes de carga k6 cobrindo página inicial, endpoints de API, fluxos de checkout e rastreamentos completos de mapas de sites. Explore nossos serviços de engenharia de back-end para saber como construímos para obter desempenho desde o primeiro dia.
Escrito por
ECOSIRE Research and Development Team
Construindo produtos digitais de nível empresarial na ECOSIRE. Compartilhando insights sobre integrações Odoo, automação de e-commerce e soluções de negócios com IA.
Artigos Relacionados
API Rate Limiting: Patterns and Best Practices
Master API rate limiting with token bucket, sliding window, and fixed counter patterns. Protect your backend with NestJS throttler, Redis, and real-world configuration examples.
GoHighLevel API and Webhooks: Custom Integrations
Complete developer guide to GoHighLevel API and webhooks. Build custom integrations, sync data with external systems, and extend GHL capabilities with REST API and webhook automation.
NestJS 11 Enterprise API Patterns
Master NestJS 11 enterprise patterns: guards, interceptors, pipes, multi-tenancy, and production-ready API design for scalable backend systems.
Mais de Performance & Scalability
Nginx Production Configuration: SSL, Caching, and Security
Nginx production configuration guide: SSL termination, HTTP/2, caching headers, security headers, rate limiting, reverse proxy setup, and Cloudflare integration patterns.
Odoo Performance Tuning: PostgreSQL and Server Optimization
Expert guide to Odoo 19 performance tuning. Covers PostgreSQL configuration, indexing, query optimization, Nginx caching, and server sizing for enterprise deployments.
Odoo vs Acumatica: Cloud ERP for Growing Businesses
Odoo vs Acumatica compared for 2026: unique pricing models, scalability, manufacturing depth, and which cloud ERP fits your growth trajectory.
Testing and Monitoring AI Agents in Production
A complete guide to testing and monitoring AI agents in production environments. Covers evaluation frameworks, observability, drift detection, and incident response for OpenClaw deployments.
Compliance Monitoring Agents with OpenClaw
Deploy OpenClaw AI agents for continuous compliance monitoring. Automate regulatory checks, policy enforcement, audit trail generation, and compliance reporting.
Optimizing AI Agent Costs: Token Usage and Caching
Practical strategies for reducing AI agent operational costs through token optimization, caching, model routing, and usage monitoring. Real savings from production OpenClaw deployments.