You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot中正确注册Micrometer Counter适配所有MeterRegistry

问题:Micrometer Counter无法同时在/actuator/metrics和/actuator/prometheus正常展示

场景复现

首次尝试:构造函数注入MeterRegistry注册Counter

在服务构造函数中注入MeterRegistry并直接注册Counter:

@Service
public class MyService {

    private final Counter fooCounter;

    public MyService(MeterRegistry meterRegistry) {
        fooCounter = Counter.builder("foo.count")
            .baseUnit("nr of foos")
            .register(meterRegistry);
    }

    public void foo() {
        fooCounter.increment();
    }
}

此时/actuator/metrics/foo.count能正常显示指标,但/actuator/prometheus中看不到该指标(其他默认指标正常)。

第二次尝试:实现MeterBinder接口

让服务类实现MeterBinder接口,在bindTo方法中注册Counter:

@Service
public class MyService implements MeterBinder {

    private Counter fooCounter;

    public MyService() {}

    public void foo() {
        fooCounter.increment();
    }

    @Override
    public void bindTo(MeterRegistry meterRegistry) {
        fooCounter = Counter.builder("foo.count")
            .baseUnit("nr of foos")
            .register(meterRegistry);
    }
}

此时/actuator/prometheus中能看到指标,但出现了两个Counter实例:调用foo()方法时只有一个实例被递增,/actuator/metrics/foo.count的数值始终不更新。

环境信息

  • Spring Boot版本:2.7.15
  • Micrometer版本:1.9.14
  • 自定义了PrometheusMeterRegistry作为Primary Bean:
@Configuration
public class MetricsConfig {

    @Bean
    @Primary
    public MeterRegistry metricRegistry() {
        return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
    }
}

解决方案

核心原因

Spring Boot默认会维护一个CompositeMeterRegistry作为主注册器,它会包含所有创建的子MeterRegistry(比如用于/actuator/metrics的SimpleMeterRegistry、用于Prometheus导出的PrometheusMeterRegistry)。你自定义的PrometheusMeterRegistry被标记为@Primary,导致注入的是单个Prometheus实例而非Composite实例,指标无法同步到所有注册器;而实现MeterBinder时,若没有基于Composite注册器绑定,会为每个子注册器创建独立的Counter实例,导致数值不同步。

方案1:移除自定义MeterRegistry Bean(推荐)

引入micrometer-registry-prometheus依赖后,Spring Boot会自动创建完整的注册器体系:Composite主注册器+子注册器。此时直接注入MeterRegistry(实际是Composite实例),注册的Counter会自动同步到所有子注册器:

@Service
public class MyService {

    private final Counter fooCounter;

    public MyService(MeterRegistry meterRegistry) {
        fooCounter = Counter.builder("foo.count")
            .baseUnit("nr of foos")
            .register(meterRegistry);
    }

    public void foo() {
        fooCounter.increment();
    }
}

修改后,/actuator/metrics/foo.count和/actuator/prometheus会同时正常展示且数值保持同步。

方案2:保留自定义配置,构建Composite注册器

如果需要自定义PrometheusMeterRegistry的配置,不要直接将其设为@Primary,而是手动构建CompositeMeterRegistry并包含所有需要的子注册器:

@Configuration
public class MetricsConfig {

    @Bean
    public PrometheusMeterRegistry prometheusMeterRegistry() {
        // 这里可以添加自定义配置,比如设置命名规则等
        return new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
    }

    @Bean
    @Primary
    public CompositeMeterRegistry compositeMeterRegistry(PrometheusMeterRegistry prometheusMeterRegistry) {
        CompositeMeterRegistry composite = new CompositeMeterRegistry();
        composite.add(prometheusMeterRegistry);
        composite.add(new SimpleMeterRegistry()); // 用于/actuator/metrics端点的注册器
        return composite;
    }
}

服务类依然用构造函数注入MeterRegistry,注册的Counter会同步到所有子注册器中。

方案3:正确实现MeterBinder接口

若坚持使用MeterBinder,需确保服务类被Spring正确识别为Bean,且基于Composite注册器绑定(避免为每个子注册器创建独立实例):

@Service
public class MyService implements MeterBinder {

    private Counter fooCounter;

    @Override
    public void bindTo(MeterRegistry meterRegistry) {
        fooCounter = Counter.builder("foo.count")
            .baseUnit("nr of foos")
            .register(meterRegistry);
    }

    public void foo() {
        fooCounter.increment();
    }
}

此时Spring会自动将MeterBinder绑定到Composite主注册器,Counter只会被创建一次,且指标会同步到所有子注册器。

内容的提问来源于stack exchange,提问作者Joel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:08:11