Integração de Histogramas no Spring Boot com Prometheus para Análise de Percentis

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)

Tags: spring-boot Prometheus micrometer Grafana java-monitoring

Publicado em 9-14 06:06