源码精读:MetricsEndpoint 与 Micrometer 集成机制详解

🔍 源码精读:MetricsEndpoint 与 Micrometer 集成机制详解

Spring Boot Actuator 的 /actuator/metrics 端点是应用性能监控的核心入口。它背后的实现依赖于 Micrometer —— 一个应用指标的“门面”(Facade)库,支持 Prometheus、Graphite、Datadog 等多种监控系统。

本文将带你深入 MetricsEndpoint 源码,解析 Micrometer 是如何与 Actuator 无缝集成的,理解从指标注册、采集到暴露的完整流程。


一、整体架构概览

+------------------+       +---------------------+
| 业务代码          |       | Micrometer         |
| @Timed, Counter   |<----->| MeterRegistry       |
+------------------+       +----------+----------+
                                      |
                                      v
                       +----------------------------+
                       | MetricsEndpoint (Actuator) |
                       | - /actuator/metrics        |
                       | - /actuator/metrics/<name> |
                       +--------------+-------------+
                                      |
                                      v
                               HTTP 响应(JSON)

✅ 核心:MeterRegistry 是 Micrometer 的核心,MetricsEndpoint 通过它读取指标数据。


二、关键类:MetricsEndpoint

📁 源码位置:

org.springframework.boot.actuate.metrics.MetricsEndpoint

🎯 核心功能:

  • 提供 /actuator/metrics/actuator/metrics/{requiredMetricName} 两个端点
  • 列出所有可用指标名称
  • 查询特定指标的测量值(measurements)和标签(tags)

1. 端点定义(@Endpoint

@Endpoint(id = "metrics")
public class MetricsEndpoint {

    private final MeterRegistry meterRegistry;

    public MetricsEndpoint(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @ReadOperation
    public MetricNames listNames() {
        Set<String> names = this.meterRegistry.getMeters().stream()
            .map(Meter::getId)
            .map(Meter.Id::getName)
            .collect(Collectors.toSet());
        return new MetricNames(names);
    }

    @ReadOperation
    public Metric getMetric(@Selector String requiredMetricName,
                           @Nullable String[] tagParameters) {
        Collection<Meter> meters = this.meterRegistry.find(requiredMetricName).meters();
        if (meters.isEmpty()) {
            throw new NoSuchMeterException(requiredMetricName);
        }
        // 聚合 measurements 和 tags
        return new Metric(meters, getTagFilters(tagParameters));
    }
}

✅ 关键点:MetricsEndpoint 本身不生成指标,而是MeterRegistry 中读取


三、Micrometer 核心:MeterRegistry

📁 源码位置:

io.micrometer.core.instrument.MeterRegistry

🎯 核心职责:

  • 注册和管理所有 Meter(如 Counter、Timer、Gauge)
  • 提供指标的读取接口
  • 将指标导出到后端监控系统(Prometheus、StatsD 等)

关键方法:

Collection<Meter> getMeters();                    // 获取所有 Meter
Meter find(String name);                         // 查找 Meter
Counter counter(String name, Tags tags);         // 创建 Counter
Timer timer(String name, Tags tags);             // 创建 Timer

MetricsEndpoint 通过 @Autowired MeterRegistry 获取指标数据。


四、集成流程:从注解到指标暴露

示例:使用 @Timed 注解

@Timed("http.server.requests")
@GetMapping("/api/user/{id}")
public User getUser(@PathVariable String id) {
    // 业务逻辑
}

这个注解是如何被转换为指标并暴露在 /actuator/metrics 中的?


1. @Timed 注解处理:MeterRegistry + AOP

关键类:MeterRegistryPostProcessor
  • 在 Spring 容器启动时,扫描所有 @Timed@Counted@Gauge 注解
  • 使用 AOP 动态代理 包装目标方法
源码路径:
org.springframework.boot.actuate.metrics.annotation.TimedAspect
@Around("@annotation(timed)")
public Object time(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
    Timer timer = registry.timer(timed.value(), tags); // 创建 Timer
    return timer.record(() -> pjp.proceed());          // 执行并记录时间
}

✅ 效果:每次调用方法时,自动记录执行时间。


2. 指标注册到 MeterRegistry

当第一次调用 @Timed 方法时:

Timer timer = MeterRegistry.counter("http.server.requests", "method", "GET", "uri", "/api/user/{id}");
  • MeterRegistry 内部维护一个 ConcurrentMap<Meter.Id, Meter> 缓存
  • 相同 name + tags 的 Meter 只创建一次

3. MetricsEndpoint 读取指标

访问 /actuator/metrics/http.server.requests 时:

Collection<Meter> meters = meterRegistry.find("http.server.requests").meters();

返回:

{
  "name": "http.server.requests",
  "measurements": [
    { "statistic": "COUNT", "value": 150 },
    { "statistic": "TOTAL_TIME", "value": 2.34 },
    { "statistic": "MEAN", "value": 0.0156 }
  ],
  "availableTags": [
    { "tag": "method", "values": ["GET", "POST"] },
    { "tag": "uri", "values": ["/api/user/1", "/login"] }
  ]
}

availableTags 来自所有匹配 Meter 的标签集合。


五、MeterRegistry 的自动配置

📁 配置类:MetricsAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore({CompositeMeterRegistryAutoConfiguration.class, SimpleMetricsExportAutoConfiguration.class})
@Import({MetricsAnnotationConfiguration.class, MeterRegistryPostProcessorConfiguration.class})
public class MetricsAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MeterRegistry meterRegistry() {
        return new SimpleMeterRegistry(); // 默认实现
    }
}
  • MetricsAnnotationConfiguration:启用 @Timed 等注解支持
  • MeterRegistryPostProcessorConfiguration:注册 AOP 切面

不同监控系统的 MeterRegistry 实现

监控系统依赖MeterRegistry 实现
Prometheusmicrometer-registry-prometheusPrometheusMeterRegistry
Graphitemicrometer-registry-graphiteGraphiteMeterRegistry
Datadogmicrometer-registry-datadogDatadogMeterRegistry
JMXmicrometer-registry-jmxJmxMeterRegistry

✅ 当你引入 micrometer-registry-prometheus,Spring Boot 会自动配置 PrometheusMeterRegistry,并暴露 /actuator/prometheus 端点。


六、Prometheus 端点是如何工作的?

📁 类:PrometheusScrapeEndpoint

@Endpoint(id = "prometheus")
public class PrometheusScrapeEndpoint {

    private final CollectorRegistry collectorRegistry;

    @ReadOperation
    public String scrape() throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        TextFormat.write004(baos, collectorRegistry.metricFamilySamples());
        return baos.toString(StandardCharsets.UTF_8.name());
    }
}
  • PrometheusMeterRegistry 将指标写入 CollectorRegistry
  • PrometheusScrapeEndpoint 将其格式化为 Prometheus 可抓取的文本格式

访问 /actuator/prometheus 返回:

# HELP http_server_requests_seconds  
# TYPE http_server_requests_seconds histogram
http_server_requests_seconds_count{method="GET",uri="/api/user/1"} 150.0
http_server_requests_seconds_sum{method="GET",uri="/api/user/1"} 2.34

七、源码调用链路总结

1. 应用启动
   ↓
2. MetricsAutoConfiguration 创建 MeterRegistry
   ↓
3. TimedAspect 扫描 @Timed 注解 → AOP 代理
   ↓
4. 方法调用 → Timer.record() → MeterRegistry 记录指标
   ↓
5. 访问 /actuator/metrics
   ↓
6. MetricsEndpoint.meterRegistry.find(name) → 获取 Meter
   ↓
7. 聚合 measurements 和 tags → JSON 返回

八、关键设计思想

特性说明
门面模式(Facade)Micrometer 统一 API,屏蔽后端差异
AOP 织入无侵入式指标采集(@Timed
延迟注册Meter 懒加载,避免启动开销
标签驱动所有指标支持多维度 tags
SPI 扩展支持多种 MeterRegistry 实现

九、如何调试这个流程?

  1. 断点位置

    • TimedAspect.time():观察 AOP 是否生效
    • SimpleMeterRegistry.register():查看 Meter 注册
    • MetricsEndpoint.getMetric():查看指标读取
  2. 验证指标

    • 调用一次接口
    • 访问 /actuator/metrics/http.server.requests
    • 检查 COUNT 是否 +1

✅ 总结:Micrometer 与 Actuator 集成核心

组件作用
MeterRegistry指标注册中心,Micrometer 的核心
@Timed / AOP无侵入式指标采集
MetricsEndpointMeterRegistry 中的指标暴露为 HTTP 端点
PrometheusScrapeEndpoint特殊格式导出,供 Prometheus 抓取
自动配置根据 classpath 自动装配合适的 MeterRegistry

📘 收获

  • 理解了 MetricsEndpoint 不是“生成”指标,而是“读取” MeterRegistry 中的数据。
  • 掌握了 @Timed 是如何通过 AOP 与 Micrometer 集成的。
  • 明白了为什么引入 micrometer-registry-prometheus 就能自动暴露 /actuator/prometheus
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值