Contexto e Necessidade de Métricas de Latência
Para garantir a confiabilidade de serviços Java, apenas médias de tempo de resposta são insuficientes. É crucial monitorar percentis como P99 e P90 para identificar caudas de latência que afetam a experiência do usuário. Enquanto a integração básica do Spring Boot com Prometheus é comum, a configuração padrão não expõe histogramas necessários para esses cálculos.
Tipos de Métricas no Micrometer
O Micrometer abstrai a coleta de métricas e classifica os dados em tipos específicos. Para análise de distribuição, dois tipos são fundamentais:
- DistributionSummary: Rastreia a distribuição de eventos arbitrários, como tamanho de payload.
- Timer: Uma especialização do DistributionSummary focada em medições de tempo (duração), permitindo conversão de unidades.
Ambos suportam a geração de histograms (buckets) e percentiles (quantis). Os buckets agrupam valores em faixas (ex: menos que 512ms), enquanto os percentis indicam valores limites para uma porcentagem da população (ex: 99% das requisições foram mais rápidas que X).
Habilitando Histogramas e Percentis
Por padrão, o atuador do Spring Boot não ativa essas configurações para evitar overhead. É necessário customizar o MeterRegistry para aplicar filtros específicos em métricas do tipo TIMER.
Primeiro, asesgure a dependência do registry do Prometheus:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
Em seguida, crie uma classe de configuração para ajustar as estatísticas de distribuição. O exemplo abaixo configura buckets e percentis para requisições HTTP e circuit breakers:
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
@Configuration
public class PrometheusHistogramConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> configureDistributionMetrics() {
return meterRegistry -> {
meterRegistry.config().meterFilter(new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id meterId, DistributionStatisticConfig defaultConfig) {
boolean isTimer = meterId.getType() == Meter.Type.TIMER;
boolean matchesPattern = meterId.getName().matches("^(http|hystrix).*");
if (isTimer && matchesPattern) {
return DistributionStatisticConfig.builder()
.percentilesHistogram(true)
.percentiles(0.50, 0.90, 0.95, 0.99)
.sla(Duration.ofMillis(50).toNanos(),
Duration.ofMillis(100).toNanos(),
Duration.ofMillis(200).toNanos(),
Duration.ofSeconds(1).toNanos(),
Duration.ofSeconds(5).toNanos())
.minimumExpectedValue(Duration.ofMillis(1).toNanos())
.maximumExpectedValue(Duration.ofSeconds(5).toNanos())
.build()
.merge(defaultConfig);
}
return defaultConfig;
}
});
};
}
}
Após aplicar essa configuração e reiniciar a aplicação, o endpoint /actuator/prometheus passará a expor séries temporais adicionais contendo tags le (para histogramas) e quantile (para percentis).
Visualização no Grafana
Para consumir esses dados no Grafana, utilize consultas PromQL que filtrem pelas tags de quantil. O exemplo abaixo calcula a média do tempo de resposta em milissegundos para os percentis 50, 90 e 99, exculindo rotas de saúde e monitoramento:
avg(http_server_requests_seconds{service=~"$service", quantile=~"0.5|0.9|0.99", uri!~"/actuator.*|/health|/prometheus"} * 1000 > 0) by (uri, quantile)