📚 Pré-requisitos Teóricos: este projeto aplica conceitos ensinados em Módulo 11: Qualidade e Testes de Software. Recomendado revisar antes de começar.
Guia do Projeto: Teste de Carga Visual (Java 21, Gatling, Grafana)
https://start.spring.ioMavenJava3.3.0 (ou a versão estável mais recente que suporte Java 21)com.exampleservico-a (repita o processo com servico-b para o outro módulo)servico-aJar21Spring Web: (Para criar controladores REST)Spring Boot Actuator: (Para expor endpoints de gerenciamento, como /health e /metrics)Prometheus: (Para formatar as métricas do Actuator para o Prometheus).zip baixado. Esta é a estrutura base do seu projeto.Este método é ideal para automação e evita a interface gráfica.
servico-aExecute o seguinte comando no seu terminal. Ele baixa e descompacta o projeto.
# Baixa o .zip
curl https://start.spring.io/starter.zip \
-d type=maven-project \
-d language=java \
-d platformVersion=3.3.0 \
-d javaVersion=21 \
-d groupId=com.example \
-d artifactId=servico-a \
-d name=servico-a \
-d packaging=jar \
-d dependencies=web,actuator,prometheus \
-o servico-a.zip
# Descompacta
unzip servico-a.zip -d servico-a
rm servico-a.zip
servico-b# Baixa o .zip
curl https://start.spring.io/starter.zip \
-d type=maven-project \
-d language=java \
-d platformVersion=3.3.0 \
-d javaVersion=21 \
-d groupId=com.example \
-d artifactId=servico-b \
-d name=servico-b \
-d packaging=jar \
-d dependencies=web,actuator,prometheus \
-o servico-b.zip
# Descompacta
unzip servico-b.zip -d servico-b
rm servico-b.zip
File > New > Project....Spring Initializr no painel esquerdo.servico-aJavaMavencom.examplecom.example.servicoaSDK 21Next.Spring WebSpring Boot ActuatorPrometheusCreate.Ctrl+Shift+P (Windows/Linux) ou Cmd+Shift+P (macOS).Spring Initializr: Create a Maven Project....3.3.0).Java.com.example.servico-a.Jar.21.Enter após selecionar):
Spring WebSpring Boot ActuatorPrometheusprojeto-teste-carga/
├── .gitignore
├── docker-compose.yml <-- (Módulo 2: Orquestrador do Prometheus e Grafana)
├── prometheus.yml <-- (Módulo 2: Configuração de scrape do Prometheus)
|
├── servico-a/ <-- (Módulo 1: Spring Boot API de Pedidos)
│ ├── .mvn/
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml <-- (Dependências: Web, Actuator, Prometheus)
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── servicoa/
│ │ │ ├── PedidoController.java
│ │ │ └── ServicoAApplication.java
│ │ └── resources/
│ │ └── application.properties <-- (server.port=8081)
│ └── test/
│ └── ...
│
├── servico-b/ <-- (Módulo 1: Spring Boot API de Estoque)
│ ├── .mvn/
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml <-- (Dependências: Web, Actuator, Prometheus)
│ └── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── servicob/
│ │ │ ├── EstoqueController.java
│ │ │ └── ServicoBApplication.java
│ │ └── resources/
│ │ └── application.properties <-- (server.port=8082)
│ └── test/
│ └── ...
│
└── gatling-teste/ <-- (Módulo 3: Projeto de Teste de Carga)
├── .mvn/
├── mvnw
├── mvnw.cmd
├── pom.xml <-- (Dependências do Gatling, ex: gatling-maven-plugin)
└── src/
└── test/
├── resources/
│ └── gatling.conf <-- (Configurações opcionais do Gatling)
└── scala/
└── simulations/
└── CargaApiSimulation.scala <-- (Script do teste de carga)
Este guia detalha a criação de um ambiente para testes de carga visuais em tempo real, monitorando a latência e a taxa de transferência entre dois microsserviços Spring Boot (Java 21).
Utilizaremos uma arquitetura desacoplada para simular um cenário real. O Gatling ataca o servico-a, que por sua vez depende do servico-b. O Prometheus coleta métricas de ambos os serviços, e o Grafana exibe o dashboard.
flowchart TD
subgraph "Ferramentas"
Gatling("Gatling Engine")
Grafana("Grafana Dashboard")
Prometheus("Prometheus Scraper")
end
subgraph "Backend (Java 21 / Spring Boot)"
SvcA["Serviço A: Pedidos<br/>(Porta: 8081)"]
SvcB["Serviço B: Estoque<br/>(Porta: 8082)"]
end
Gatling -- 1. Carga HTTP (Usuários Virtuais) --> SvcA
SvcA -- 2. Chamada REST interna --> SvcB
Prometheus -- 3. Scrape (métricas) --> SvcA
Prometheus -- 3. Scrape (métricas) --> SvcB
Grafana -- 4. Query (PromQL) --> Prometheus
Criaremos dois serviços. Ambos devem ter as mesmas dependências do Micrometer para expor métricas ao Prometheus.
Adicione a todos os serviços:
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
Serviço B (Estoque): servico-b/src/main/resources/application.properties
# Porta do Serviço B
server.port=8082
# Nome da Aplicação (para métricas)
spring.application.name=servico-b
# Ativa Virtual Threads (Java 21+)
spring.threads.virtual.enabled=true
# Expor o endpoint /actuator/prometheus
management.endpoints.web.exposure.include=prometheus,health
management.endpoint.health.show-details=always
Serviço A (Pedidos): servico-a/src/main/resources/application.properties
# Porta do Serviço A
server.port=8081
# Nome da Aplicação (para métricas)
spring.application.name=servico-a
# Ativa Virtual Threads (Java 21+)
spring.threads.virtual.enabled=true
# Expor o endpoint /actuator/prometheus
management.endpoints.web.exposure.include=prometheus,health
management.endpoint.health.show-details=always
# URL do serviço dependente
app.servico-b.url=http://localhost:8082
Simula uma operação custosa (ex: consulta em banco de dados) com um atraso.
// servico-b/src/main/java/com/example/servicob/EstoqueController.java
package com.example.servicob;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
@RestController
public class EstoqueController {
@GetMapping("/estoque/{produtoId}")
public String getEstoque(@PathVariable String produtoId) {
try {
// Simula latência variável (ex: I/O de banco)
long delay = ThreadLocalRandom.current().nextLong(50, 200);
Thread.sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "Produto " + produtoId + ": 10 unidades";
}
}
Chama o Serviço B usando RestTemplate (ou WebClient).
// servico-a/src/main/java/com/example/servicoa/PedidoController.java
package com.example.servicoa;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
@RestController
public class PedidoController {
private final RestTemplate restTemplate;
private final String servicoBUrl;
public PedidoController(RestTemplateBuilder builder, @Value("${app.servico-b.url}") String servicoBUrl) {
this.restTemplate = builder.build();
this.servicoBUrl = servicoBUrl;
}
// Endpoint principal que será testado pelo Gatling
@GetMapping("/fazer-pedido")
public String fazerPedido() {
// Simula a busca por um produto aleatório
int produtoId = new Random().nextInt(1000);
// Chama o Serviço B
String estoqueInfo = restTemplate.getForObject(
servicoBUrl + "/estoque/" + produtoId,
String.class
);
return "Pedido criado. Info: " + estoqueInfo;
}
}
Usaremos Docker Compose para subir o Prometheus e o Grafana.
prometheus.yml)Crie este arquivo na raiz do projeto. Ele instrui o Prometheus a “raspar” (scrape) as métricas dos endpoints /actuator/prometheus dos nossos serviços.
# prometheus.yml
global:
scrape_interval: 10s # Intervalo de coleta
scrape_configs:
# Coleta métricas do Serviço A (Pedidos)
- job_name: 'servico-a'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['host.docker.internal:8081'] # 'host.docker.internal' permite ao Docker acessar o host local
# Coleta métricas do Serviço B (Estoque)
- job_name: 'servico-b'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['host.docker.internal:8082']
docker-compose.yml)Este arquivo orquestra a pilha de monitoramento.
# docker-compose.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
# Permite que o container acesse os serviços no host
extra_hosts:
- "host.docker.internal:host-gateway"
grafana:
image: grafana/grafana-oss:latest
container_name: grafana
ports:
- "3000:3000"
depends_on:
- prometheus
environment:
# Define o usuário/senha padrão
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
Gatling usa Scala. A estrutura de pastas deve ser:
gatling-teste/
src/
test/
scala/
simulations/
CargaApiSimulation.scala <-- Nosso script de teste
Este script simula 100 usuários acessando o endpoint /fazer-pedido simultaneamente por 30 segundos.
// CargaApiSimulation.scala
package simulations
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class CargaApiSimulation extends Simulation {
// 1. Configuração do Protocolo HTTP
val httpProtocol = http
.baseUrl("http://localhost:8081") // URL base do Serviço A
.acceptHeader("application/json")
.userAgentHeader("Gatling Load Test")
// 2. Definição do Cenário
val scn = scenario("Cenário de Carga API Pedidos")
.exec(
http("Req: Fazer Pedido")
.get("/fazer-pedido")
.check(status.is(200)) // Verifica se a resposta é HTTP 200
)
// 3. Configuração da Carga (Injeção de Usuários)
setUp(
scn.inject(
// rampUsersPerSec(10).to(100).during(10.seconds), // Rampa de subida
// nothingFor(5.seconds), // Pausa
constantUsersPerSec(100).during(30.seconds) // Carga constante
)
).protocols(httpProtocol)
}
Inicie ambos os serviços Spring Boot (em terminais separados ou pela IDE):
# Terminal 1: Iniciar Serviço B (Estoque)
cd servico-b/
./mvnw spring-boot:run
# Terminal 2: Iniciar Serviço A (Pedidos)
cd servico-a/
./mvnw spring-boot:run
Verificação: Acesse http://localhost:8081/actuator/prometheus e http://localhost:8082/actuator/prometheus. Você deve ver as métricas.
# Terminal 3: Iniciar Prometheus e Grafana
docker-compose up
Verificação:
http://localhost:9090 (Prometheus).http://localhost:3000 (Grafana). Login: admin/admin.http://localhost:3000).http://prometheus:9090 (Grafana e Prometheus estão na mesma rede Docker).Painel 1: Taxa de Requisições (RPS) no Serviço A
Use a query PromQL para medir a taxa de requisições HTTP no servico-a.
// Mede a taxa de requisições por segundo (RPS) no endpoint /fazer-pedido
rate(http_server_requests_seconds_count{job="servico-a", uri="/fazer-pedido"}[1m])
Painel 2: Latência P95 (Serviço A vs Serviço B)
Use esta query para ver a latência (percentil 95) das chamadas.
// Latência P95 do endpoint /fazer-pedido (visão do Svc A)
histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{job="servico-a", uri="/fazer-pedido"}[1m])) by (le))
// Latência P95 da chamada interna (visão do Svc B)
histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{job="servico-b", uri="/estoque/{produtoId}"}[1m])) by (le))
Painel 3: Requisições com Erro (HTTP 5xx)
// Taxa de erros 5xx no Serviço A
rate(http_server_requests_seconds_count{job="servico-a", status=~"5.*"}[1m])
gatling.sh).# Terminal 4: Executar o Teste
cd gatling-teste/
# Se estiver usando o plugin Maven do Gatling
./mvnw gatling:test -Dgatling.simulationClass=simulations.CargaApiSimulation
http://localhost:3000). Você verá os gráficos de RPS subindo, a latência aumentando e, eventualmente, erros (se o sistema não suportar a carga).No seu editor/IDE, abra a pasta deste projeto (File > Open Folder) ou navegue via terminal:
cd infra_nginx_03_gatling
terraform init && terraform plan
# ou docker-compose up -d
[!TIP] Dica para execução a partir da raiz do repositório: Se você abriu o repositório completo no VS Code, basta navegar até a pasta antes de executar: cd catalogo_projetos/infra_nginx_03_gatling
O diagrama abaixo ilustra a topologia de componentes, balanceamento, esteiras de automação e observabilidade na subpasta gatling-teste/:
flowchart TD
subgraph Config["Configuração & IaC"]
C1["Manifestos Declarativos"] --> C2["Variáveis & Segredos"]
end
subgraph Orquestracao["Orquestração & Redes"]
O1["Load Balancer / Ingress"] --> O2["Pods / Contêineres de Serviço"]
end
subgraph Observabilidade["Observabilidade & Segurança"]
M1["Telemetria Prometheus / k6"] --> M2["Scanners SAST & Dashboards"]
end
subgraph Qualidade["Homologação & Qualidade"]
Q1["Automação CI/CD"] --> Q2["Checkpoints CP-01..05 PASS"]
end
Config --> Orquestracao
Orquestracao --> Observabilidade
Observabilidade --> Qualidade
style Config fill:#e3f2fd,stroke:#1976d2,stroke-width:1.5px
style Orquestracao fill:#fffde7,stroke:#fbc02d,stroke-width:1.5px
style Observabilidade fill:#e8f5e9,stroke:#388e3c,stroke-width:1.5px
style Qualidade fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1.5px
| ID | Persona | História de Usuário | Critério de Aceite | Prioridade |
|---|---|---|---|---|
| US-01 | Engenheiro de Performance | Desejo simular 2.000 usuários concorrentes via cenários Gatling | Ramp-up progressivo e injeção de requisições com métricas de percentis p95 e p99 | Alta |
| US-02 | Operador de Observabilidade | Desejo exportar métricas do Nginx e serviços para Prometheus | Coleta contínua de latências, taxa de requisições e códigos HTTP de resposta | Alta |
| US-03 | Analista de SRE | Desejo dashboard interativo no Grafana com gráficos de RPS e erros | Painel visual em tempo real comparando desempenho de serviços concorrentes | Alta |
| US-04 | Arquiteto de Software | Desejo identificar ponto de degradação e gargalos antes do deploy | Relatórios HTML gerados pelo Gatling com quebra detalhada de transações | Média |
| US-05 | Auditor de Qualidade | Desejo asserções automáticas de SLA falhando a esteira se p99 > 200ms | Gatling assertions configuradas no script Scala/Java da simulação | Média |
| ID | Descrição do Requisito | Criticidade |
|---|---|---|
| RF-01 | Cenário de simulação Gatling com injeção de usuários e asserções de SLA | Alta |
| RF-02 | Exportador de métricas Prometheus integrado com Nginx e microsserviços | Alta |
| RF-03 | Dashboard provisionado no Grafana com visualização de saturação e throughput | Alta |
| RF-04 | Dois serviços de backend (A e B) com características de latência distintas | Média |
| RF-05 | Subida unificada de todo o stack de teste e telemetria via Docker Compose | Média |
| ID | Especificação Arquitetural | Criticidade |
|---|---|---|
| RNF-01 | Precisão estatística na medição de latência sem distorções de medição | Alta |
| RNF-02 | Baixo consumo de CPU do agente gerador de carga durante o teste | Alta |
| RNF-03 | Isolamento dos testes na pasta gatling-teste/ | Alta |
| RNF-04 | Conformidade com práticas de testes de carga contínuos em CI/CD | Média |
| RNF-05 | Dashboards e alertas com persistência configurada via volumes | Média |
| User Story | Requisito Funcional | Requisito Não-Funcional | Módulo / Artefato de Código | Status de Homologação |
|---|---|---|---|---|
| US-01 | RF-01 |
RNF-01, RNF-03 |
gatling-teste/simulations/ |
Testado |
| US-02 | RF-02 |
RNF-01, RNF-05 |
prometheus.yml |
Testado |
| US-03 | RF-03 |
RNF-02, RNF-05 |
docker-compose.yml |
Testado |
| US-04 | RF-04 |
RNF-01, RNF-04 |
servico.a/ & servico.b/ |
Testado |
| US-05 | RF-01 |
RNF-01, RNF-04 |
gatling-teste/simulations/ |
Testado |
| Checkpoint | Escopo da Validação | Artefato Verificado | Status |
|---|---|---|---|
| CP-01 | Configuração Declarativa do Ambiente | gatling-teste/ |
PASS ✅ |
| CP-02 | Provisionamento & Orquestração | Deployments / Services |
PASS ✅ |
| CP-03 | Redes, Proxies & Segurança de Borda | Ingress / Security Groups |
PASS ✅ |
| CP-04 | Observabilidade, Métricas & SAST | Metrics / Scanners |
PASS ✅ |
| CP-05 | Homologação com Suíte de Testes | Test Suite / CI Gate |
PASS ✅ |