Meter / MeterRegistry / MeterBinder 指标体系
概述
Micrometer 是 Spring Boot 官方推荐的指标收集门面,其核心类族围绕 Meter、MeterRegistry、MeterBinder 三个角色构建。Meter 定义 5 种基本度量类型(Timer、Counter、Gauge、DistributionSummary、LongTaskTimer),MeterRegistry 负责注册和管理 Meter 实例,MeterBinder 提供自动绑定机制将内置指标注册到 Registry。
本文将深入拆解 Micrometer 指标体系的 10 个关键细节,涵盖 Meter 类型体系、注册表桥接、过滤器、AOP 注解、Prometheus 导出等核心内容。
本文基于 Micrometer 1.12.x + Spring Boot 3.2.5 源码分析。
基础使用可参考 Micrometer + Prometheus 监控体系。
1. Meter 的 5 种基本类型
Meter 是 Micrometer 中所有度量的抽象父类,定义了 5 种具体实现类型。
public abstract class Meter {
private final Id id;
protected Meter(Id id) {
this.id = id;
}
public Id getId() { return this.id; }
// 5 种具体的 Meter 子类型
public enum Type {
COUNTER, // 计数器:单调递增
GAUGE, // 仪表盘:可增可减的瞬时值
LONG_TASK_TIMER, // 长任务计时器:跟踪当前正在执行的任务
TIMER, // 计时器:记录耗时和次数
DISTRIBUTION_SUMMARY // 分布摘要:记录事件的分布情况
}
}5 种 Meter 对比:
| 类型 | 说明 | 典型场景 | 数据输出 |
|---|---|---|---|
Counter | 单调递增计数器 | 请求总数、错误总数 | http_requests_total |
Gauge | 可增可减的瞬时值 | 当前线程数、内存使用量 | jvm_memory_used_bytes |
Timer | 记录耗时和调用次数 | 请求延迟、数据库查询耗时 | http_request_duration_seconds |
DistributionSummary | 事件分布统计 | 响应体大小、订单金额分布 | http_response_size_bytes |
LongTaskTimer | 正在执行的任务耗时 | 长时间运行的后台任务 | jvm_gc_pause_seconds |
创建方式:
MeterRegistry registry = new SimpleMeterRegistry();
// Counter
Counter counter = Counter.builder("my.counter")
.description("请求总数")
.register(registry);
// Timer
Timer timer = Timer.builder("my.timer")
.publishPercentileHistogram(true)
.register(registry);
// Gauge(通过 lambda 创建)
Gauge gauge = Gauge.builder("my.gauge", pool, ThreadPoolExecutor::getActiveCount)
.register(registry);
// DistributionSummary
DistributionSummary summary = DistributionSummary.builder("my.summary")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
// LongTaskTimer
LongTaskTimer longTaskTimer = LongTaskTimer.builder("my.long.task")
.register(registry);2. Meter.Id 的组成
Meter.Id 是 Meter 的唯一标识,由 5 个基本属性组成。
public class Meter {
public static final class Id {
private final String name; // 指标名称(如 "http.server.requests")
private final Tags tags; // 标签列表(如 method="GET", status="200")
private final Type type; // Meter 类型(Timer / Counter / ...)
private final String baseUnit; // 基本单位(如 "seconds"、"bytes")
private final String description; // 描述信息
public Id(String name, Tags tags, String baseUnit, String description, Type type) {
this.name = name;
this.tags = Tags.concat(tags); // Tags 自动排序
this.baseUnit = baseUnit;
this.description = description;
this.type = type;
}
// 通过 Tag 的自动排序保证 Meter 注册的一致性
// 无论 Tag 的添加顺序如何,最终生成的 Id 是相同的
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Id id = (Id) o;
return name.equals(id.name)
&& tags.equals(id.tags) // Tags 已排序
&& type == id.type
&& Objects.equals(baseUnit, id.baseUnit)
&& Objects.equals(description, id.description);
}
@Override
public int hashCode() {
return Objects.hash(name, tags, type, baseUnit, description);
}
}
}Id 的匹配规则:
Meter.Id 的 equals/hashCode 基于:
name + tags + type + baseUnit + description
↓
相同的 Id = 同一个 Meter 实例(不允许重复注册)
↓
第二次 register() 返回已存在的 Meter 实例示例:
// 两个 Id 等价(即使 tag 顺序不同)
Meter.Id id1 = new Meter.Id("http.requests", Tags.of("method", "GET", "status", "200"), ...);
Meter.Id id2 = new Meter.Id("http.requests", Tags.of("status", "200", "method", "GET"), ...);
// id1.equals(id2) → true(Tags 内部排序)全局 Tags(commonTags):
MeterRegistry registry = new SimpleMeterRegistry();
registry.config().commonTags("application", "my-app", "env", "prod");
// 所有通过该 registry 注册的 Meter 都会自动附加 application 和 env 标签3. MeterRegistry 的 createId() 方法
MeterRegistry 通过 createId() 方法创建 Meter.Id,并在创建过程中合并公共标签。
public abstract class MeterRegistry {
private final List<MeterFilter> filters; // Meter 过滤器链
private final List<Tag> commonTags; // 公共标签
protected Meter.Id createId(String name, Tags tags, String baseUnit, String description) {
// 1. 合并公共标签
Tags mergedTags = Tags.concat(commonTags, tags);
// 2. 应用 MeterFilter(过滤、重命名、修改 tag)
// MeterRegistryPostProcessor 已将所有 MeterFilter 添加到 config()
MeterFilterResult result = applyFilters(name, mergedTags, baseUnit, description);
// 3. 如果过滤器拒绝了该 Meter,返回 null
if (result.isDenied()) {
return null;
}
// 4. 创建 Id(内部自动对 Tags 排序)
return new Meter.Id(result.getName(), result.getTags(),
result.getBaseUnit(), result.getDescription(), getMeterType());
}
// 子类根据 Id 创建或获取已存在的 Meter
protected abstract <M extends Meter> M registerMeterIfNecessary(Meter.Id id, M meter);
}创建完整流程:
MeterRegistry.timer("http.server.requests", "method", "GET")
↓
createId("http.server.requests", Tags.of("method", "GET"), null, null)
↓
① 合并 commonTags → Tags.of("method", "GET", "application", "my-app")
↓
② 应用 MeterFilter 链
├── filter.map(...) → 重命名
├── filter.replaceTagValues(...) → 替换标签值
└── filter.deny(...) → 拒绝
↓
③ 生成 Meter.Id(Tags 已排序)
↓
④ registerMeterIfNecessary(id, timer)
├── 已存在 → 返回已注册的 Timer
└── 不存在 → 创建新 Timer 并注册4. CompositeMeterRegistry 的桥接模式
CompositeMeterRegistry 是 Micrometer 的核心设计模式实现——桥接模式(Bridge),它组合多个底层 MeterRegistry,实现一次指标注册、多处导出。
public class CompositeMeterRegistry extends MeterRegistry {
private final List<MeterRegistry> registries = new CopyOnWriteArrayList<>();
public CompositeMeterRegistry() {
super(new Clock.SystemClock());
}
// 添加底层注册表
public void add(MeterRegistry registry) {
if (registry != this) {
this.registries.add(registry);
}
}
// 移除底层注册表
public void remove(MeterRegistry registry) {
this.registries.remove(registry);
}
@Override
protected <M extends Meter> M registerMeterIfNecessary(Meter.Id id, M meter) {
// 将 Meter 注册到所有底层 Registry
for (MeterRegistry registry : this.registries) {
registry.registerMeterIfNecessary(id, meter);
}
return meter;
}
@Override
protected void publish() {
// 每个底层 Registry 自行管理发布策略
for (MeterRegistry registry : this.registries) {
registry.publish();
}
}
}MetricsAutoConfiguration 中的使用:
@Configuration
public class MetricsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public CompositeMeterRegistry compositeMeterRegistry(
List<MeterRegistry> registries) {
CompositeMeterRegistry composite = new CompositeMeterRegistry();
// 将所有 MeterRegistry(Prometheus、Graphite、Datadog 等)添加到 Composite
for (MeterRegistry registry : registries) {
composite.add(registry);
}
return composite;
}
}桥接模式示意图:
应用代码
↓
CompositeMeterRegistry ← 应用中直接使用的注册表
↓ ↓ ↓
Prometheus Graphite Datadog ← 底层注册表
MeterRegistry MeterRegistry
↓
Scrape() / Push() / Push()MeterRegistryPostProcessor:
// 在 Bean 初始化后,将 MeterFilter、commonTags 等配置同步到所有底层注册表
public class MeterRegistryPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof MeterRegistry registry) {
// 应用 MeterFilter
for (MeterFilter filter : this.filters) {
registry.config().filter(filter);
}
// 应用 commonTags
registry.config().commonTags(this.commonTags);
}
return bean;
}
}5. MeterBinder.bindTo(MeterRegistry) 注册
MeterBinder 是 Micrometer 的 SPI 接口,自动将内置指标(JVM、CPU、Tomcat 等)绑定到 MeterRegistry。
@FunctionalInterface
public interface MeterBinder {
void bindTo(MeterRegistry registry);
}MetricsAutoConfiguration 自动注册:
@Configuration
public class MetricsAutoConfiguration {
@Bean
public MeterRegistryPostProcessor meterRegistryPostProcessor(
List<MeterBinder> binders) {
// 自动收集所有 MeterBinder Bean
return new MeterRegistryPostProcessor(binders);
}
}
// MeterRegistryPostProcessor 遍历所有 MeterBinder
public class MeterRegistryPostProcessor {
private final List<MeterBinder> binders;
public void postProcess(MeterRegistry registry) {
for (MeterBinder binder : this.binders) {
binder.bindTo(registry);
}
}
}内置的 MeterBinder 实现:
| MeterBinder | 注册的指标 |
|---|---|
JvmGcMetrics | jvm.gc.pause、jvm.gc.concurrent.phase.time、jvm.gc.max.data.size |
JvmMemoryMetrics | jvm.memory.used.bytes、jvm.memory.committed.bytes、jvm.memory.max.bytes |
JvmThreadMetrics | jvm.threads.live、jvm.threads.daemon、jvm.threads.peak |
ProcessorMetrics | system.cpu.count、system.cpu.usage、process.cpu.usage |
TomcatMetrics | tomcat.sessions.active、tomcat.threads.busy、tomcat.threads.current |
JdbcConnectionPoolMetrics | jdbc.connections.active、jdbc.connections.max、jdbc.connections.min |
LogbackMetrics | logback.events(按级别:debug、info、warn、error) |
UptimeMetrics | process.uptime、process.start.time |
FileDescriptorMetrics | process.files.open、process.files.max |
自定义 MeterBinder:
@Component
public class CustomMetricsBinder implements MeterBinder {
@Override
public void bindTo(MeterRegistry registry) {
// 注册自定义指标
Gauge.builder("myapp.active.users", userService, UserService::getActiveUserCount)
.description("当前活跃用户数")
.register(registry);
Counter.builder("myapp.orders.created")
.description("订单创建总数")
.register(registry);
}
}6. MeterFilter 的 4 种过滤
MeterFilter 提供了强大的指标过滤和转换能力,支持在注册前对 Meter.Id 进行修改或拒绝。
@FunctionalInterface
public interface MeterFilter {
// 1. 名称过滤
MeterFilter nameStartsWith(String prefix);
MeterFilter nameStartsWithIgnoreCase(String prefix);
// 2. 标签过滤
MeterFilter replaceTagValues(String tagKey, Function<String, String> replacement);
MeterFilter tag(String tagKey, String tagValue);
// 3. 接受/拒绝
MeterFilter denyUnless(MeterFilter predicate);
MeterFilter deny() / accept();
// 4. 自定义转换
Meter.Id map(Meter.Id id);
default MeterFilterReply accept(Meter.Id id) { /* DENY / NEUTRAL / ACCEPT */ }
}4 种过滤模式:
| 过滤方式 | 方法 | 说明 |
|---|---|---|
| 重命名 | MeterFilter.map(id -> ...) | 修改 Meter.Id(改名、添加标签、修改单位) |
| 接受 | MeterFilter.accept() | 接受该 Meter |
| 拒绝 | MeterFilter.deny() | 拒绝该 Meter(不会注册到 Registry) |
| 替换值 | MeterFilter.replaceTagValues(key, func) | 将指定 tag 的值通过函数替换 |
使用示例:
@Configuration
public class MeterFilterConfig {
@Bean
public MeterFilter meterFilter() {
return new MeterFilter() {
@Override
public Meter.Id map(Meter.Id id) {
// 1. 重命名:将 http.server.requests 改为 http.requests
if (id.getName().equals("http.server.requests")) {
return id.withName("http.requests");
}
return id;
}
@Override
public MeterFilterReply accept(Meter.Id id) {
// 2. 拒绝以 "jvm.gc.concurrent" 开头的指标
if (id.getName().startsWith("jvm.gc.concurrent")) {
return MeterFilterReply.DENY;
}
// 3. 仅接受 "jvm.*" 和 "http.*" 指标
if (id.getName().startsWith("jvm.") || id.getName().startsWith("http.")) {
return MeterFilterReply.ACCEPT;
}
return MeterFilterReply.NEUTRAL;
}
@Override
public MeterFilterReply accept(Meter.Id id) {
// 4. 替换标签值:将 method tag 中的大写转为小写
return MeterFilter.replaceTagValues("method", String::toLowerCase);
}
};
}
}在 application.yml 中的通用配置:
management:
metrics:
enable:
# 关闭特定指标的自动注册
jvm: true
tomcat: true
hikaricp: false
tags:
# 全局通用标签
application: ${spring.application.name:unknown}
environment: ${spring.profiles.active:default}7. @Timed 的 AOP 代理
@Timed 注解通过 TimedAspect 实现 AOP 代理,自动记录方法的执行耗时和调用次数。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
String value() default ""; // 指标名称前缀
String[] extraTags() default {}; // 额外标签
boolean longTask() default false; // 是否使用 LongTaskTimer
double[] percentiles() default {}; // 百分位数
boolean histogram() default false; // 是否发布直方图
String description() default ""; // 描述
}TimedAspect 实现:
@Aspect
public class TimedAspect {
private final MeterRegistry registry;
public TimedAspect(MeterRegistry registry) {
this.registry = registry;
}
@Around("@annotation(timed)")
public Object timedMethod(ProceedingJoinPoint pjp, Timed timed) throws Throwable {
// 1. 确定指标名称
String metricName = timed.value().isEmpty()
? pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName()
: timed.value();
// 2. 创建 Timer
Timer.Builder timerBuilder = Timer.builder(metricName)
.tags(timed.extraTags())
.description(timed.description());
if (timed.histogram()) {
timerBuilder.publishPercentileHistogram();
}
if (timed.percentiles().length > 0) {
timerBuilder.publishPercentiles(timed.percentiles());
}
Timer timer = timerBuilder.register(registry);
// 3. 采样并执行
Timer.Sample sample = Timer.start(registry);
try {
return pjp.proceed();
} finally {
// 4. 记录耗时
sample.stop(timer);
}
}
}使用示例:
@RestController
public class UserController {
@Timed(value = "user.get", percentiles = {0.5, 0.95, 0.99})
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@Timed(extraTags = {"type", "batch"})
@PostMapping("/users/batch")
public List<User> batchCreate(@RequestBody List<User> users) {
return userService.batchCreate(users);
}
}TimedAspect 的自动配置:
@Configuration
@ConditionalOnClass({Aspect.class, TimedAspect.class})
public class TimedAspectAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}
}生成的指标:
# 每个 @Timed 方法产生一组 Timer 指标
user_get_seconds_count{exception="none"} 150 ← 调用次数
user_get_seconds_sum{exception="none"} 45.2 ← 总耗时
user_get_seconds_max{exception="none"} 2.1 ← 最大耗时
user_get_seconds_percentile{phi="0.5"} 0.15 ← P50
user_get_seconds_percentile{phi="0.95"} 0.89 ← P95
user_get_seconds_percentile{phi="0.99"} 1.52 ← P998. Timer 的直方图 / SLO
Timer 支持直方图和 SLO(Service Level Objective)配置,用于分析请求延迟分布。
Timer timer = Timer.builder("myapp.request.duration")
// 1. 百分比直方图(自动计算 P50、P95、P99)
.publishPercentileHistogram(true)
// 2. 指定百分位数
.publishPercentiles(0.5, 0.75, 0.9, 0.95, 0.99, 0.999)
// 3. SLO(服务等级目标)- 自定义桶边界
.sla(
Duration.ofMillis(50), // 50ms
Duration.ofMillis(100), // 100ms
Duration.ofMillis(200), // 200ms
Duration.ofMillis(500), // 500ms
Duration.ofSeconds(1) // 1s
)
// 4. 最小期望值
.minimumExpectedValue(Duration.ofMillis(1))
// 5. 最大期望值
.maximumExpectedValue(Duration.ofSeconds(10))
.register(registry);Timer.Builder 内部实现:
public class Timer {
public static class Builder {
private boolean publishPercentileHistogram;
private double[] percentiles;
private Duration[] sla;
private Duration min;
private Duration max;
public Timer register(MeterRegistry registry) {
// 根据配置创建直方图桶
if (publishPercentileHistogram || percentiles.length > 0 || sla.length > 0) {
// 创建 CumulativeHistogram 或 FixedBoundaryHistogram
Histogram histogram = createHistogram();
return new Timer(id, histogram, ...);
}
return new Timer(id, null, ...); // 无直方图
}
private Histogram createHistogram() {
HistogramConfig config = new HistogramConfig()
.sla(sla) // SLO 桶边界
.min(TimeUnit.MILLISECONDS.toNanos(min.toMillis()))
.max(TimeUnit.MILLISECONDS.toNanos(max.toMillis()));
// 如果需要百分比直方图,使用 PercentileHistogram
if (publishPercentileHistogram) {
return new PercentileHistogram(config);
}
// 如果只需要指定百分位数,使用 FixedBoundaryHistogram
return new FixedBoundaryHistogram(config);
}
}
}Prometheus 中的直方图输出:
# TYPE myapp_request_duration_seconds histogram
myapp_request_duration_seconds_count 1000 ← 总请求数
myapp_request_duration_seconds_sum 452.3 ← 总耗时
myapp_request_duration_seconds_bucket{le="0.05"} 100 ← ≤50ms
myapp_request_duration_seconds_bucket{le="0.1"} 350 ← ≤100ms
myapp_request_duration_seconds_bucket{le="0.2"} 650 ← ≤200ms
myapp_request_duration_seconds_bucket{le="0.5"} 900 ← ≤500ms
myapp_request_duration_seconds_bucket{le="1.0"} 980 ← ≤1s
myapp_request_duration_seconds_bucket{le="+Inf"} 1000 ← 总
myapp_request_duration_seconds_percentile{phi="0.5"} 0.12 ← P50
myapp_request_duration_seconds_percentile{phi="0.95"} 0.45 ← P95
myapp_request_duration_seconds_percentile{phi="0.99"} 0.89 ← P999. Counter 的单调递增计数
Counter 只支持单调递增(不能减少),适用于请求计数、错误计数等场景。
public interface Counter extends Meter {
// 增加 1
default void increment() {
increment(1.0);
}
// 增加指定数量
void increment(double amount);
}
// 默认实现
public class SimpleCounter implements Counter {
private final AtomicDouble value; // 使用 CAS 保证线程安全
public SimpleCounter(Id id) {
super(id);
this.value = new AtomicDouble(0.0);
}
@Override
public void increment(double amount) {
if (amount < 0) {
throw new IllegalArgumentException("Counter 不能递减");
}
// CAS 循环,保证并发安全
for (;;) {
double current = this.value.get();
double newValue = current + amount;
if (this.value.compareAndSet(current, newValue)) {
break;
}
}
}
@Override
public double count() {
return this.value.get();
}
}使用示例:
@Service
public class MetricsService {
private final Counter requestCounter;
private final Counter errorCounter;
public MetricsService(MeterRegistry registry) {
this.requestCounter = Counter.builder("api.requests.total")
.description("API 请求总数")
.tag("service", "user-service")
.register(registry);
this.errorCounter = Counter.builder("api.requests.errors")
.description("API 请求错误数")
.register(registry);
}
public void handleRequest() {
requestCounter.increment();
try {
// 业务处理
} catch (Exception e) {
errorCounter.increment();
throw e;
}
}
}Prometheus 输出:
# HELP api_requests_total API 请求总数
# TYPE api_requests_total counter
api_requests_total{service="user-service"} 12345
# HELP api_requests_errors API 请求错误数
# TYPE api_requests_errors counter
api_requests_errors 23注意:
Counter不可递减,如果数值需要下降,应使用Gauge。Counter在 Prometheus 中配合rate()函数使用才是有效的。
10. PrometheusMeterRegistry 的 OpenMetrics 格式
PrometheusMeterRegistry 继承 MeterRegistry,负责将 Micrometer 指标序列化为 Prometheus 文本格式。
public class PrometheusMeterRegistry extends MeterRegistry {
private final PrometheusConfig config;
private final CollectorRegistry prometheusRegistry; // Prometheus 原生注册表
public PrometheusMeterRegistry(PrometheusConfig config) {
this(config, CollectorRegistry.defaultRegistry, Clock.SYSTEM);
}
// 核心方法:将 Meter 转换为 Prometheus Collector
@Override
protected <M extends Meter> M registerMeterIfNecessary(Meter.Id id, M meter) {
// 根据 Meter 类型创建对应的 Prometheus Collector
switch (id.getType()) {
case COUNTER:
// Counter → prometheus.client.Counter
buildAndRegisterCounter(id, (Counter) meter);
break;
case TIMER:
// Timer → prometheus.client.Histogram
buildAndRegisterHistogram(id, (Timer) meter);
break;
case GAUGE:
// Gauge → prometheus.client.Gauge
buildAndRegisterGauge(id, (Gauge) meter);
break;
case DISTRIBUTION_SUMMARY:
// DistributionSummary → prometheus.client.Summary
buildAndRegisterSummary(id, (DistributionSummary) meter);
break;
}
return meter;
}
// 暴露 /actuator/prometheus 端点
public String scrape() {
// 遍历所有 Collector,输出 Prometheus 文本格式
Writer writer = new StringWriter();
TextFormat.write004(writer, prometheusRegistry.metricFamilySamples()); // OpenMetrics 格式
return writer.toString();
}
}spring-boot-starter-actuator 中的集成:
@Configuration
@ConditionalOnClass(CollectorRegistry.class)
public class PrometheusMetricsExportAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public PrometheusMeterRegistry prometheusMeterRegistry(
PrometheusConfig config, CollectorRegistry collectorRegistry) {
return new PrometheusMeterRegistry(config, collectorRegistry, Clock.SYSTEM);
}
@Bean
public PrometheusScrapeEndpoint prometheusScrapeEndpoint(
PrometheusMeterRegistry registry) {
// 暴露 /actuator/prometheus 端点
return new PrometheusScrapeEndpoint(registry);
}
}输出格式示例:
# HELP jvm_memory_used_bytes JVM 已使用的内存量
# TYPE jvm_memory_used_bytes gauge
jvm_memory_used_bytes{area="heap",id="G1 Survivor Space"} 5242880
jvm_memory_used_bytes{area="heap",id="G1 Eden Space"} 2.68435456E8
jvm_memory_used_bytes{area="nonheap",id="Metaspace"} 1.32448448E8
# HELP http_server_requests_seconds
# TYPE http_server_requests_seconds histogram
http_server_requests_seconds_count{method="GET",status="200"} 1000
http_server_requests_seconds_sum{method="GET",status="200"} 45.2
http_server_requests_seconds_bucket{method="GET",status="200",le="0.1"} 500
http_server_requests_seconds_bucket{method="GET",status="200",le="+Inf"} 1000格式支持:
| 格式 | 方法 | 说明 |
|---|---|---|
text/v0.0.4 | TextFormat.write004() | OpenMetrics 标准格式(推荐) |
text/v0.0.3 | TextFormat.write003() | Prometheus 旧格式 |
总结
Meter / MeterRegistry / MeterBinder 指标体系的 10 个细节点总结如下:
| # | 细节点 | 核心类/机制 |
|---|---|---|
| ① | Meter 的 5 种基本类型 | Timer / Counter / Gauge / DistributionSummary / LongTaskTimer |
| ② | Meter.Id 的组成 | name + tags + type + baseUnit + description |
| ③ | MeterRegistry 的 createId() 方法 | 合并 commonTags → 应用 MeterFilter → 生成唯一的 Meter.Id |
| ④ | CompositeMeterRegistry 的桥接模式 | addRegistry() → 添加底层 PrometheusMeterRegistry / GraphiteMeterRegistry |
| ⑤ | MeterBinder.bindTo(MeterRegistry) 注册 | JvmGcMetrics / JvmMemoryMetrics / JvmThreadMetrics / ProcessorMetrics / TomcatMetrics |
| ⑥ | MeterFilter 的 4 种过滤 | map() → 重命名 / accept() → 接受 / deny() → 拒绝 / replaceTagValues() → 替换 |
| ⑦ | @Timed 的 AOP 代理 | TimedAspect.timedMethod() → Timer.Sample.start() → sample.stop(timer) |
| ⑧ | Timer 的直方图 / SLO | publishPercentileHistogram(true) / sla(Duration.ofMillis(100)) |
| ⑨ | Counter 的单调递增计数 | increment() → AtomicDouble CAS 更新 |
| ⑩ | PrometheusMeterRegistry 的 OpenMetrics 格式 | scrape() → TextFormat.write004() 暴露指标 |