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

如何仅手动触发Micrometer Gauge指标采集?

问题

我需要监控数据库中的一个可增可减的数值,因此使用了Micrometer的Gauge。由于没有对应实例变量,我采用方法引用而非对象引用,但Gauge会自动定期调用该方法。出于性能考量,我不想让方法被周期性调用,请问如何仅按需触发Gauge?

原代码

TestApplication.java

@SpringBootApplication
public class TestApplication {

  @Autowired private TestController testController;
  @Autowired private MeterRegistry meterRegistry;

  public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
  }

  @Bean
  public Gauge myGauge() {
    return Gauge.builder("test_gauge", testController::calc).register(meterRegistry);
  }
}

TestController.java

@RestController
@Slf4j
public class TestController {

  public Double calc() {

    // read value from database
    Double d = RandomUtils.nextDouble();
    log.info("Called with value {}", d);

    return d;
  }

  @GetMapping(path = "/call")
  public String call(@RequestParam(name = "value") Integer value) {

    // save value to database

    // trigger gauge

    return "ok";
  }
}

现象

日志

当Prometheus抓取指标时,calc()方法每15秒被调用一次:

2022-12-16 15:12:17.556  INFO 6260 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
2022-12-16 15:12:18.039  INFO 6260 --- [nio-8081-exec-1] test.TestController                      : Called with value 1.1228275083891679E306
2022-12-16 15:12:32.778  INFO 6260 --- [nio-8081-exec-2] test.TestController                      : Called with value 6.136924711044749E307
2022-12-16 15:12:44.798  INFO 6260 --- [nio-8081-exec-3] test.TestController                      : Called with value 4.914434799158517E307
2022-12-16 15:12:58.095  INFO 6260 --- [nio-8081-exec-5] test.TestController                      : Called with value 1.2183342676183758E308

指标

Actuator端点prometheus返回的Gauge指标:

# HELP test_gauge  
# TYPE test_gauge gauge
test_gauge{application="test-service",} 1.2183342676183758E308

解决方案

核心思路是维护一个缓存的数值变量,让Gauge监控这个变量,仅在数据更新时主动刷新该变量的值,避免Gauge周期性调用数据库查询方法。

修改步骤

  1. 在TestController中添加线程安全的缓存变量,用于存储当前数据库数值,同时在初始化和数据更新时刷新缓存:
@RestController
@Slf4j
public class TestController {
    // 用AtomicDouble保证并发场景下的线程安全
    private AtomicDouble cachedValue = new AtomicDouble(0.0);

    // 初始化时从数据库加载初始值
    @PostConstruct
    public void init() {
        Double initialValue = loadFromDatabase();
        cachedValue.set(initialValue);
        log.info("Initial value loaded from DB: {}", initialValue);
    }

    // 实际从数据库查询数值的逻辑
    private Double loadFromDatabase() {
        return RandomUtils.nextDouble();
    }

    // 保存数值到数据库并刷新缓存
    @GetMapping(path = "/call")
    public String call(@RequestParam(name = "value") Integer value) {
        // 执行保存到数据库的逻辑
        saveToDatabase(value);
        
        // 保存后重新查询数据库,更新缓存
        Double newValue = loadFromDatabase();
        cachedValue.set(newValue);
        log.info("Updated cached value to: {}", newValue);
        
        return "ok";
    }

    // 实际保存数值到数据库的逻辑
    private void saveToDatabase(Integer value) {
        // 业务代码实现
    }

    // 给Gauge提供获取缓存值的方法
    public Double getCachedValue() {
        return cachedValue.get();
    }
}
  1. 修改TestApplication中的Gauge配置,让它监控缓存值的获取方法:
@SpringBootApplication
public class TestApplication {

  @Autowired private TestController testController;
  @Autowired private MeterRegistry meterRegistry;

  public static void main(String[] args) {
    SpringApplication.run(TestApplication.class, args);
  }

  @Bean
  public Gauge myGauge() {
    // 监控内存中的缓存值,而非直接调用数据库查询
    return Gauge.builder("test_gauge", testController::getCachedValue)
            .register(meterRegistry);
  }
}

原理说明

  • 缓存变量cachedValue使用AtomicDouble,避免多线程更新时的并发问题。
  • 仅在初始化和数据更新(调用/call接口)时执行数据库查询,更新缓存值。
  • Gauge在Prometheus抓取指标时只会调用getCachedValue()读取内存缓存,不会触发数据库操作,性能开销可以忽略。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 09:05:16