🔍 源码精读: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 实现 |
|---|---|---|
| Prometheus | micrometer-registry-prometheus | PrometheusMeterRegistry |
| Graphite | micrometer-registry-graphite | GraphiteMeterRegistry |
| Datadog | micrometer-registry-datadog | DatadogMeterRegistry |
| JMX | micrometer-registry-jmx | JmxMeterRegistry |
✅ 当你引入
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将指标写入CollectorRegistryPrometheusScrapeEndpoint将其格式化为 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 实现 |
九、如何调试这个流程?
-
断点位置:
TimedAspect.time():观察 AOP 是否生效SimpleMeterRegistry.register():查看 Meter 注册MetricsEndpoint.getMetric():查看指标读取
-
验证指标:
- 调用一次接口
- 访问
/actuator/metrics/http.server.requests - 检查
COUNT是否 +1
✅ 总结:Micrometer 与 Actuator 集成核心
| 组件 | 作用 |
|---|---|
MeterRegistry | 指标注册中心,Micrometer 的核心 |
@Timed / AOP | 无侵入式指标采集 |
MetricsEndpoint | 将 MeterRegistry 中的指标暴露为 HTTP 端点 |
PrometheusScrapeEndpoint | 特殊格式导出,供 Prometheus 抓取 |
| 自动配置 | 根据 classpath 自动装配合适的 MeterRegistry |
📘 收获:
- 理解了
MetricsEndpoint不是“生成”指标,而是“读取”MeterRegistry中的数据。 - 掌握了
@Timed是如何通过 AOP 与 Micrometer 集成的。 - 明白了为什么引入
micrometer-registry-prometheus就能自动暴露/actuator/prometheus。

1984

被折叠的 条评论
为什么被折叠?



