如何仅手动触发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周期性调用数据库查询方法。
修改步骤
- 在
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(); } }
- 修改
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
相关产品推荐
相关产品推荐

