📚 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 proj_aplicacoes_full_stack/projetos/infra_nginx_03_gatling