DEBUG.WIKI
搬瓦工;先看线路,再选 VPS;CN2 GIA · 对应线路套餐;查看线路 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
RackNerd;$21.99/年 · 1GB KVM

Micrometer 1.17.0 启动缺少 PauseDetector,检查 Timer Bean 的反射路径

Timer Bean 反射触发 PauseDetector 缺类时,核对 Micrometer 1.17.0 的可选依赖字段,采用 1.17.1 修正或调整计时器注册边界。

Spring Boot 从 4.0.7 升级到 4.1.0 后,应用在处理 Timer Bean 时启动失败,异常最内层是:

Failed to introspect Class [io.micrometer.core.instrument.AbstractTimer]
NoClassDefFoundError: org/LatencyUtils/PauseDetector

没有配置暂停补偿,也不等于这条缺类错误不可能发生。Micrometer 1.17.0 把 LatencyUtils 变成可选依赖,但 AbstractTimer 的字段仍直接声明为它的类型;Spring 在反射读取字段时,会触发该类型的解析。

应同时看完整堆栈和依赖图。若经过 PersistenceAnnotationBeanPostProcessor、ReflectionUtils.getDeclaredFields,且项目把 Timer 单独注册成 Bean,与这条路径相符。业务自己创建暂停检测器而缺依赖,是另一种情况。

修复已在 Micrometer 1.17.1 中

1.17.1 的正式源码把这个字段改成 Object,避免反射枚举字段时必须解析可选的 LatencyUtils 类型。需要采用包含修正且与 Boot 版本兼容的依赖组合,重新打包应用。

不要只把 micrometer-core 随意降成另一个年代的版本。Boot 的监控、注册器与 observation 依赖有各自的版本约束;单独换一个 JAR,可能把缺类错误变成方法签名错误。

可以先查看实际工程解析到的组合:

./mvnw dependency:tree -Dincludes=io.micrometer:micrometer-core,org.latencyutils:LatencyUtils

命令用于 Maven 工程,Windows 使用 mvnw.cmd。Gradle 工程应查看自己的 runtimeClasspath,不是照抄 Maven 命令。还要核对最终部署归档,不要仅确认 IDE 中已经下载了新版本。

不必为每个计时器都声明一个 Bean

若暂时不能更新,可以评估去掉不必要的 Timer Bean,让需要计时的业务组件注入 MeterRegistry,再注册自己的计时器:

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;

final class TaskMetrics {
    private final Timer elapsed;

    TaskMetrics(MeterRegistry registry) {
        this.elapsed = Timer.builder("tasks.elapsed")
                .tag("kind", "batch")
                .register(registry);
    }

    void record(Runnable task) {
        elapsed.record(task);
    }
}

这只是普通组件的计时片段。项目可按原来的方式注册 TaskMetrics,但不要再把它内部的 Timer 另行暴露成 @Bean。同一 registry 中使用相同指标名与标签会复用对应计时器;动态标签则需要按具体业务值注册,不能把不断变化的用户标识塞进标签。

这项调整改变的是 Bean 处理边界,不是开启或关闭暂停补偿。若其他代码仍反射 Timer 的字段,移除一个 Bean 并不能保证那些路径也消失。

另一种短期办法是在 Boot 管理的依赖范围内显式补回 org.latencyutils:LatencyUtils。先确认版本管理确实提供版本,并评估它是临时兼容依赖还是业务实际需要的能力;升级后再决定是否移除,不要留下一份不明用途的永久补丁。

恢复标准除了应用完成启动,还包括原指标能够累计、Prometheus 等原输出端仍能读取、标签未发生意外变化。若只看到启动成功,却没有执行原来的计时业务,尚未覆盖这次调整的主要行为。