You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot 2集成Micrometer注册自定义指标阻塞问题求助

遇到这种构造函数中注册Micrometer指标导致线程挂起的问题,我之前在老版本Spring Boot+Micrometer组合中也碰到过,给你几个可行的解决方案:

1. 避开Bean构造阶段,用Spring生命周期钩子注册指标

不要在构造函数里直接调用meterRegistry.counter(),因为此时Spring上下文还在初始化过程中,MeterRegistry可能正被Spring用来注册自身的70多个内置指标,很容易引发锁竞争。改用@PostConstruct注解的初始化方法,或者实现ApplicationListener<ContextRefreshedEvent>,等Spring上下文完全就绪后再注册自定义指标:

public class MetricsAwareActiveTasksRepository {
    private final ActiveTasksRepository activeTasksRepository;
    private final MeterRegistry meterRegistry;
    private Counter createdTaskIdsCounter;
    private Counter autoStoppedTasksCounter;

    // 构造函数只做依赖注入,不碰指标注册
    public MetricsAwareActiveTasksRepository(ActiveTasksRepository activeTasksRepository, MeterRegistry meterRegistry) {
        this.activeTasksRepository = activeTasksRepository;
        this.meterRegistry = meterRegistry;
    }

    @PostConstruct
    public void initCustomMetrics() {
        this.createdTaskIdsCounter = meterRegistry.counter(CustomBusinessMetrics.CREATED_TASK_IDS_COUNTER);
        this.autoStoppedTasksCounter = meterRegistry.counter(CustomBusinessMetrics.AUTO_STOPPED_TASKS_COUNTER);
    }
}

2. 升级依赖版本(最推荐)

你用的Spring Boot 2.0.2和Micrometer 1.0.4都是比较老旧的版本,这类同步块死锁/阻塞的问题在后续的补丁版本中已经被修复了。建议升级到Spring Boot 2.0.x的最新稳定补丁版(比如2.0.9.RELEASE),或者直接升级到更高的大版本(比如2.1.x及以上),对应的Micrometer版本也会同步升级,从根源上解决这类兼容性问题。

3. 改用MeterBinder接口注册指标

Spring Boot支持自动识别并注册实现了MeterBinder接口的Bean,这种方式更符合Spring的生命周期管理,能避免手动注册时的线程问题:

@Component
public class TaskBusinessMetrics implements MeterBinder {
    private final ActiveTasksRepository activeTasksRepository;

    public TaskBusinessMetrics(ActiveTasksRepository activeTasksRepository) {
        this.activeTasksRepository = activeTasksRepository;
    }

    @Override
    public void bindTo(MeterRegistry meterRegistry) {
        // 在这里注册所有自定义指标
        Counter.builder(CustomBusinessMetrics.CREATED_TASK_IDS_COUNTER)
                .description("Total number of created task IDs")
                .register(meterRegistry);
        Counter.builder(CustomBusinessMetrics.AUTO_STOPPED_TASKS_COUNTER)
                .description("Total number of auto-stopped tasks")
                .register(meterRegistry);
    }
}

4. 排查自定义MeterFilter的问题

如果你配置了自定义的MeterFilter,检查一下configure方法里有没有耗时操作或者阻塞逻辑——因为在Micrometer的同步块中会调用所有过滤器的configure方法,一旦某个过滤器卡住,整个线程就会挂起。可以先临时移除自定义过滤器,看看问题是否消失,以此排查根源。

为什么构造函数里会出问题?

本质上是线程锁竞争:Spring初始化Bean的主线程,和Spring自身注册内置指标的线程,同时争抢MeterRegistry内部的meterMapLock锁,导致死锁或者长时间阻塞,最终线程进入无响应的ZOMBIE状态。避开Bean构造阶段,让指标注册在Spring上下文稳定后执行,就能避免这个问题。

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

火山引擎 最新活动