Spring Webflux中如何从异步值创建Micrometer Gauge指标?
从返回Mono的异步源创建Micrometer Gauge的实现方案
核心原理
Micrometer Gauge的取值逻辑是同步触发的(采集端拉取指标时会调用对应Supplier的get方法),本身不支持异步返回,直接在Supplier中调用Mono.block()会存在阻塞采集线程的风险,在Webflux环境下极易引发线程池耗尽等问题,因此推荐采用本地缓存最新值+异步更新缓存的方案实现。
方案1:原子变量缓存最新值(生产环境推荐)
这个方案是最稳妥的实现,Gauge直接读取本地原子变量的取值,异步源的结果定期/事件触发更新到原子变量中:
实现步骤
- 定义线程安全的原子变量存储最新的gauge取值
- 注册Gauge,绑定到原子变量的get方法
- 启动异步流订阅,将异步返回的结果更新到原子变量
代码示例
import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.time.Duration; import java.util.concurrent.atomic.AtomicLong; import lombok.extern.slf4j.Slf4j; @Slf4j @Component public class AsyncGaugeConfig { // 存储gauge最新值的原子变量,支持并发安全的读写 private final AtomicLong asyncGaugeValue = new AtomicLong(0); // 你的异步取值方法,替换为实际返回Mono的业务逻辑 private Mono<Long> getAsyncValue() { return Mono.just(100L); } public AsyncGaugeConfig(MeterRegistry registry) { // 注册Gauge,直接从原子变量取值 Gauge.builder("custom.async.gauge", asyncGaugeValue, AtomicLong::get) .description("异步源生成的gauge指标") .register(registry); // 启动定期更新逻辑,按需调整拉取间隔 Flux.interval(Duration.ofSeconds(15)) // 背压策略:如果更新速度慢于拉取速度,丢弃多余的拉取请求 .onBackpressureDrop() .flatMap(ignore -> getAsyncValue() // 异常处理:避免单次拉取失败导致整个流终止 .doOnError(e -> log.warn("异步gauge值拉取失败", e)) .onErrorResume(e -> Mono.empty())) // 更新原子变量的值 .subscribe(asyncGaugeValue::set); } }
适配说明
- 如果异步值是事件驱动推送的,不需要定期拉取,直接订阅对应的事件流更新原子变量即可
- 取值为double类型的话,把
AtomicLong替换为AtomicDouble即可 - IO密集型的异步取值逻辑,可以通过
subscribeOn(Schedulers.boundedElastic())指定调度器适配
方案2:Supplier中直接阻塞取值(不推荐)
仅在指标采集频率极低、异步取值耗时极短的场景下可以临时使用,必须添加超时逻辑避免阻塞采集线程:
Gauge.builder("custom.async.gauge", () -> { // 一定要设置短超时,避免无限阻塞 return getAsyncValue().block(Duration.ofMillis(200)); }) .register(registry);
风险说明
- 采集端拉取指标时会同步阻塞等待Mono返回,超时会导致该指标采集失败
- 大量这类Gauge会拖慢整个指标采集流程,甚至引发线程池耗尽
内容的提问来源于stack exchange,提问作者fyrkov
相关产品推荐
相关产品推荐

