Micrometer 系列【29】源码分析:MeterRegistry 实例化流程

文章目录

  • [1. MeterRegistry 源码解读](#1. MeterRegistry 源码解读)
    • [1.1 静态常量](#1.1 静态常量)
    • [1.2 实例成员字段](#1.2 实例成员字段)
    • [1.3 内部类 & 内部接口](#1.3 内部类 & 内部接口)
      • [1.3.1 Config](#1.3.1 Config)
      • [1.3.2 More](#1.3.2 More)
      • [1.3.3 NewMeterSupplier</M extends Meter/>](#1.3.3 NewMeterSupplier</M extends Meter/>)
    • [1.4 抽象工厂方法](#1.4 抽象工厂方法)
    • [1.5 public 方法](#1.5 public 方法)
      • [1.5.1 获取指标](#1.5.1 获取指标)
      • [1.5.2 删除、清空](#1.5.2 删除、清空)
      • [1.5.3 生命周期](#1.5.3 生命周期)
      • [1. 5.4 返回 Config 内部实例](#1. 5.4 返回 Config 内部实例)
    • [1.6 核心私有流程方法](#1.6 核心私有流程方法)
      • [1.6.1 registerMeterIfNecessary()](#1.6.1 registerMeterIfNecessary())
      • [1.6.2 getOrCreateMeter()](#1.6.2 getOrCreateMeter())
      • [1.6.3 mapId(Meter.Id id)](#1.6.3 mapId(Meter.Id id))
      • [1.6.4 accept(Meter.Id id)](#1.6.4 accept(Meter.Id id))
      • [1.6.5 checkAndWarnAboutDoubleRegistration()](#1.6.5 checkAndWarnAboutDoubleRegistration())
      • [1.6.6 isStaleId / unmarkStaleId](#1.6.6 isStaleId / unmarkStaleId)
  • [2. SimpleMeterRegistry 实例化流程](#2. SimpleMeterRegistry 实例化流程)
    • [2.1 步骤 1:进入 SimpleMeterRegistry() 无参构造器](#2.1 步骤 1:进入 SimpleMeterRegistry() 无参构造器)
    • [2.2 步骤 2:解析两个入参](#2.2 步骤 2:解析两个入参)
      • [2.2.1 SimpleConfig.DEFAULT](#2.2.1 SimpleConfig.DEFAULT)
      • [2.2.2 Clock.SYSTEM](#2.2.2 Clock.SYSTEM)
    • [2.3 步骤 3:进入双参构造器](#2.3 步骤 3:进入双参构造器)
    • [2.4 步骤 4:执行 super (clock)](#2.4 步骤 4:执行 super (clock))
    • [2.5 步骤 5:执行配置校验](#2.5 步骤 5:执行配置校验)
    • [2.6 步骤 6:保存 config 引用到成员变量](#2.6 步骤 6:保存 config 引用到成员变量)
  • [3. 添加到全局复合注册器](#3. 添加到全局复合注册器)
    • [3.1 代码示例](#3.1 代码示例)
    • [3.2 Metrics 源码](#3.2 Metrics 源码)

1. MeterRegistry 源码解读

MeterRegistryMicrometer 的核心抽象基类,SimpleMeterRegistryPrometheusMeterRegistryCompositeMeterRegistry 都是它的实现类。所有 CounterTimerGaugeDistributionSummaryMeter(度量器)全部由 MeterRegistry 创建、存放、管理。

源码要点

  1. 负责 Meter 的注册、过滤、去重、监听、删除生命周期;
  2. 定义抽象工厂方法,交给子类实现具体 Meter 实例;
  3. 处理MeterFilter过滤、ID 映射(过滤前后两套 ID 映射);
  4. 线程安全设计:ConcurrentHashMap + meterMapLock对象锁;
  5. 关闭之后返回 Noop* 空实现,指标埋点调用无副作用。

1.1 静态常量

静态字段 作用
doubleRegistrationLogger 重复注册告警日志器,Gauge/FunctionCounter/FunctionTimer重复注册输出警告
BASE_TIME_UNIT_STRING_CACHE EnumMap<TimeUnit,String>,时间单位字符串缓存,避免重复toString,性能优化

1.2 实例成员字段

字段 修饰符 类型 说明
clock protected final Clock 时钟,提供时间戳,Timer/LongTaskTimer依赖;构造器传入,子类提供实现
meterMapLock private final Object 全局对象锁;新增、删除、修改元数据都要synchronized(this.meterMapLock);读操作大多无锁,追求高并发性能
filters private volatile MeterFilter[] meter过滤器数组,volatile保证多线程可见;config.meterFilter()追加,数组拷贝生成新数组,不可变
meterAddedListeners private final CopyOnWriteArrayList<Consumer<Meter>> Meter新增事件监听器集合
meterRemovedListeners private final CopyOnWriteArrayList<Consumer<Meter>> Meter删除事件监听器集合
meterRegistrationFailedListeners private final CopyOnWriteArrayList<BiConsumer<Meter.Id,String>> 注册失败监听器
config private final Config 内部配置类实例,config()对外返回
more private final More 内部类实例,封装不常用指标API(LongTaskTimer、FunctionCounter等)
meterMap private final ConcurrentHashMap<Meter.Id,Meter> 过滤之后的Meter存储Map(mappedId → Meter) 。对外getMeters()读取来源于此;CHM保证迭代不会并发修改异常
preFilterIdToMeterMap private final HashMap<Meter.Id,Meter> 原始ID(过滤前)映射Meterkey=未经过MeterFilter处理的原始Meter.Id;写操作受meterMapLock保护,读无锁
meterToPreFilterIdMap private final HashMap<Meter,Meter.Id> 反向映射 Meter →原始Id;删除Meter时根据Meter快速找到原始pre‑filter id;锁保护
stalePreFilterIds private final HashSet<Meter.Id> 过期原始Id集合 :当MeterFilter晚于meter注册时,旧注册的原始id标记为stale;再次注册时重新执行filter映射
syntheticAssociations private final HashMap<Meter.Id,Set<Meter.Id>> 维护合成指标关联关系;删除主Meter时,把关联的合成指标一起删除;锁保护
closed private final AtomicBoolean 注册表关闭标记;关闭后不再新建Meter,全部返回Noop实现;close()CAS设置true
pauseDetector private PauseDetector 协调遗漏补偿检测器,默认NoPauseDetector.INSTANCE;用于Timer停顿补偿
highCardinalityTagsDetector private @Nullable HighCardinalityTagsDetector 高基数标签检测器,检测标签爆炸风险,可选开启
namingConvention private NamingConvention 命名转换器,默认snakeCase;把meter id转换为监控后端识别的名字

📌两套ID概念:

  • pre‑filter Id :业务传入原始Meter.Id(注册时传入)
  • mappedId :经过全部MeterFilter.map()处理之后的Id,存储到meterMap

Micrometer 维护两套映射,是为了支持注册 Meter 之后再添加 MeterFilter 的场景(不推荐,但兼容)。

1.3 内部类 & 内部接口

1.3.1 Config

通过meterRegistry.config()获取;用于配置 Registry 行为,流式 API

核心方法

  1. commonTags():全局通用标签,底层调用meterFilter(MeterFilter.commonTags())
  2. meterFilter(MeterFilter filter):追加过滤器。⚠️如果此时已经注册过 Meter ,打印警告日志,同时把所有已注册原始 id 加入stalePreFilterIds集合。
  3. onMeterAdded / onMeterRemoved / onMeterRegistrationFailed:注册事件监听器
  4. namingConvention():设置/获取命名规范
  5. clock():获取注册表时钟
  6. pauseDetector():设置停顿检测器
  7. withHighCardinalityTagsDetector():开启高基数标签爆炸检测。

内部私有方法 logWarningAboutLateFilter() :当 filtermeter 注册之后添加,打印 warn日志,同时输出堆栈(debug 级别)。

1.3.2 More

通过meterRegistry.more()获取,封装低频指标类型,避免主 API 臃肿:

  • longTaskTimer():长耗时任务计时器
  • counter()FunctionCounter函数式计数器
  • timer()FunctionTimer函数计时器
  • timeGauge()TimeGauge时间型 Gauge

1.3.3 NewMeterSupplier</M extends Meter/>

私有函数式接口,内部使用 ,注册 Meterlambda 回调。

java 复制代码
M create(MeterRegistry registry, Meter.Id id,
         DistributionStatisticConfig distributionStatisticConfig,
         PauseDetector pauseDetector);

用于把创建 Meter 的逻辑封装,在registerMeterIfNecessary统一处理重复注册、过滤、noop 分支。

1.4 抽象工厂方法

模板方法模式:父类 MeterRegistry 做注册、过滤、去重逻辑;真正创建 Meter 实例交给子类实现。

java 复制代码
protected abstract <T> Gauge newGauge(Meter.Id id, @Nullable T obj, ToDoubleFunction<T> valueFunction);
protected abstract Counter newCounter(Meter.Id id);
protected abstract Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig,PauseDetector pauseDetector);
protected abstract DistributionSummary newDistributionSummary(Meter.Id id,DistributionStatisticConfig distributionStatisticConfig, double scale);
protected abstract Meter newMeter(Meter.Id id, Meter.Type type, Iterable<Measurement> measurements);
protected abstract <T> FunctionTimer newFunctionTimer(Meter.Id id, T obj,ToLongFunction<T> countFunction,ToDoubleFunction<T> totalTimeFunction, TimeUnit totalTimeFunctionUnit);
protected abstract <T> FunctionCounter newFunctionCounter(Meter.Id id, T obj, ToDoubleFunction<T> countFunction);
protected abstract TimeUnit getBaseTimeUnit();
protected abstract DistributionStatisticConfig defaultHistogramConfig();
  • newLongTaskTimer 存在废弃重载,做向后兼容;
  • newTimeGauge不是 abstract ,提供默认实现:内部封装 Gauge ,包装成TimeGauge 匿名类。

1.5 public 方法

1.5.1 获取指标

  1. counter() / timer() / summary() / gauge() 多重载:支持可变 tagsIterableTags对象,构建 Meter.Id,调用内部注册逻辑。
  2. more() 返回 More 内部类实例。
  3. getMeters() 返回不可修改的 meter 列表;底层meterMap.values().toArray()
  4. forEachMeter()遍历所有 meter
  5. find(String name)Search,查找 meter,找不到返回 null
  6. get(String name)RequiredSearch,找不到抛 MeterNotFoundException

1.5.2 删除、清空

  1. remove(Meter meter):删除 meter ,使用 mappedId
  2. remove(Meter.Id mappedId):删除过滤后的 Id ;同时清理 3map ,处理syntheticAssociations合成指标级联删除,触发meterRemovedListeners
  3. removeByPreFilterId(Meter.Id preFilterId):按原始未过滤 Id 删除。
  4. clear() :循环调用remove删除全部 meter

1.5.3 生命周期

  1. close()CAS 设置 closed=true ;关闭所有 meter ,关闭高基数检测器;关闭之后新建 meter 全部返回 Noop 空实现。
  2. isClosed() :返回关闭状态。

1. 5.4 返回 Config 内部实例

java 复制代码
config() 

1.6 核心私有流程方法

1.6.1 registerMeterIfNecessary()

整个 MeterRegistry 最核心入口,所有 meter 注册都会走到这里。两个重载版本。

职责:

  1. 调用 getOrCreateMeter() 完成 meter 查找/创建;
  2. 校验:同一个 MeterId 不能注册不同类型 Meter ,否则抛出IllegalArgumentException

1.6.2 getOrCreateMeter()

完整注册流程:

  1. 根据原始 pre‑filter idpreFilterIdToMeterMap,命中并且不是 stale,直接返回已存在 meter,做重复注册警告。
  2. 调用mapId()执行全部MeterFilter.map()得到 mappedId,查meterMap;命中则返回,做重复注册警告。
  3. 判断注册表是否 losed → 返回 Noop*Meter
  4. 进入 synchronized(meterMapLock)临界区:双重检查锁再次判断 meterMap 是否存在该 mappedId
  5. 调用 accept(mappedId) 执行 MeterFilter.accept() ;如果返回 DENY,返回Noop
  6. 如果是 Timer/Summary类型,执行MeterFilter.configure()修改DistributionStatisticConfig配置,和defaultHistogramConfig()合并。
  7. 调用NewMeterSupplier.create()回调,执行子类的 newXXX() 抽象工厂,创建真正 Mete r实例。
  8. 处理syntheticAssociation合成指标关联。
  9. 触发meterAddedListeners监听器。
  10. 写入三张 mapmeterMappreFilterIdToMeterMapmeterToPreFilterIdMap;清除 stale 标记。

1.6.3 mapId(Meter.Id id)

遍历所有filters,调用filter.map(id)完成 id 转换。

1.6.4 accept(Meter.Id id)

遍历 MeterFilter ,判断该 Meter 是否允许注册(ACCEPT / DENY / NEUTRAL)。

1.6.5 checkAndWarnAboutDoubleRegistration()

GaugeFunctionCounterFunctionTimer 做重复注册警告;普通Counter/ Timer重复注册不会告警(幂等,直接返回已存在实例)。

1.6.6 isStaleId / unmarkStaleId

staleId 机制,解决先注册 meter后设置 MeterFilter 的兼容场景。

2. SimpleMeterRegistry 实例化流程

SimpleMeterRegistryMicrometer 内置最小实现,主要用于单元测试,不对接任何监控后端,内存内保存所有 meter 数据。

调用入口:

java 复制代码
SimpleMeterRegistry registry = new SimpleMeterRegistry();

2.1 步骤 1:进入 SimpleMeterRegistry() 无参构造器

java 复制代码
// SimpleMeterRegistry.java 行37-39
public SimpleMeterRegistry() {
    this(SimpleConfig.DEFAULT, Clock.SYSTEM);  // → 委托到双参构造器
}

此时 JVM 堆分配了对象内存,所有实例字段置默认值 null

2.2 步骤 2:解析两个入参

2.2.1 SimpleConfig.DEFAULT

是一个 lambda 实现 SimpleConfig 接口,get(key) 永远返回 null

java 复制代码
SimpleConfig DEFAULT = k -> null;

2.2.2 Clock.SYSTEM

系统时钟实现:

  • wallTime() System.currentTimeMillis() 纪元毫秒,用于 step 周期边界判断
  • monotonicTime()System.nanoTime() 单调纳秒,用于计算耗时 (Timer)
java 复制代码
// Clock.java 行19-27
Clock SYSTEM = new Clock() {
    @Override
    public long wallTime() {
        return System.currentTimeMillis();   // 挂钟时间(ms),用于时间戳和 step 边界计算
    }
    @Override
    public long monotonicTime() {
        return System.nanoTime();            // 单调递增时间(ns),用于测量时间间隔
    }
};

2.3 步骤 3:进入双参构造器

java 复制代码
// SimpleMeterRegistry.java 行41-46
public SimpleMeterRegistry(SimpleConfig config, Clock clock) {
    super(clock);                   // → 步骤4:调用父类 MeterRegistry(Clock) 构造器
    config.requireValid();          // → 步骤5:校验 SimpleConfig 有效性
    this.config = config;           // → 步骤6:存储 config 引用
}

2.4 步骤 4:执行 super (clock)

父类 MeterRegistry 构造器:

java 复制代码
// MeterRegistry.java 行154-157
protected MeterRegistry(Clock clock) {
    requireNonNull(clock);          // 断言 clock 非 null,否则抛 NPE
    this.clock = clock;             // 保存 clock 引用(Clock.SYSTEM 实例)
}

super (clock) 进入到父类构造器中时,会先把父类所有实例字段初始化完,此时 MeterRegistry 中已初始化的字段:

接着执行父类构造方法体:

java 复制代码
    requireNonNull(clock);          // 断言 clock 非 null,否则抛 NPE
    this.clock = clock;             // 保存 clock 引用(Clock.SYSTEM 实例)

2.5 步骤 5:执行配置校验

调用 SimpleConfig#validate(),校验 stepmode 参数合法性;默认配置不会抛异常。

java 复制代码
// MeterRegistryConfig.java 行44-46
default void requireValid() throws ValidationException {
    validate().orThrow();   // → 先调 validate(),若无效则抛异常
}

SimpleConfig.DEFAULT.validate() 内部逻辑:

java 复制代码
// SimpleConfig.java 行38-41
default Validated<?> validate() {
    return checkAll(this,
        check("step", SimpleConfig::step),    // 校验 "simple.step" 属性
        check("mode", SimpleConfig::mode)     // 校验 "simple.mode" 属性
    );
}

校验 step()

java 复制代码
// SimpleConfig.java 行32-34
default Duration step() {
    return getDuration(this, "step").orElse(Duration.ofMinutes(1));
}
// getDuration(this, "step") → 从 DEFAULT(k->null) 中查 "simple.step" → 返回 null
// .orElse(Duration.ofMinutes(1)) → 回退到默认值 **60 秒**

校验 mode()

java 复制代码
// SimpleConfig.java 行41-43
default CountingMode mode() {
    return getEnum(this, CountingMode.class, "mode").orElse(CountingMode.CUMULATIVE);
}
// getEnum(this, CountingMode.class, "mode") → 从 DEFAULT(k->null) 中查 "simple.mode" → 返回 null
// .orElse(CountingMode.CUMULATIVE) → 回退到默认值 **CUMULATIVE(累积模式)**

2.6 步骤 6:保存 config 引用到成员变量

✅ 到此 new SimpleMeterRegistry () 对象实例化完成。

⚠️注意:此时还没有任何指标 (Meter),registry 只是空容器。指标是后续调用Counter.builder()...register(registry) 才会被创建并注册进 registry

此时 SimpleMeterRegistry 构造完毕,关键状态:

  • this.config = SimpleConfig.DEFAULT
  • this.clock = Clock.SYSTEM
  • this.getBaseTimeUnit() = TimeUnit.SECONDS
  • this.meterMap = 空的 ConcurrentHashMap

3. 添加到全局复合注册器

3.1 代码示例

通常,一个应用只有一个 MeterRegistry 入口,可以使用 CompositeMeterRegistry 包含多个子 Registry ,实现一次埋点、到处上报 。

代码示例:

java 复制代码
Metrics.addRegistry(registry)

new JvmMemoryMetrics().bindTo(Metrics.globalRegistry);
new JvmGcMetrics().bindTo(Metrics.globalRegistry);
new JvmThreadMetrics().bindTo(Metrics.globalRegistry);
new ProcessorMetrics().bindTo(Metrics.globalRegistry);

CompositeMeterRegistry:复合模式,当你往它注册一个 Meter,它会把这个 Meter 同步注册到它内部持有的所有子 registry

3.2 Metrics 源码

MetricsMicrometer静态全局工具门面类

java 复制代码
public static final CompositeMeterRegistry globalRegistry = new CompositeMeterRegistry();

public static void addRegistry(MeterRegistry registry) {
    globalRegistry.add(registry);
}

CompositeMeterRegistry(复合注册器)本身不存储指标,内部维护一个List<MeterRegistry>列表,可以把多个实际的 registrySimpleMeterRegistry / PrometheusMeterRegistry)装进去。

相关推荐
聆风吟º2 小时前
【金仓数据库征文】Java 应用接入金仓数据库:从驱动、连接池到存储过程调试的踩坑实录
java·开发语言·数据库
似璟如你3 小时前
Java 开发者的 Go 语法基础:从 0 开始快速上手 Go
java·开发语言·后端·golang·go·编程语言
zzz_23684 小时前
TencentDB-Agent-Memory 深度解析:让多个 Agent 共享项目经验的记忆中枢
java·开发语言·jvm·人工智能·agent·memory·tencent db
vx-程序开发4 小时前
django医院预约挂号系统---附源码23353
java·javascript·spring boot·python·eclipse·django·php
金斗潼关4 小时前
ysoserial的使用
java
ruleslol4 小时前
volatile 关键字
java
名字还没想好☜4 小时前
Kubernetes NetworkPolicy 实战:默认全通的坑与用标签做零信任隔离
运维·云原生·容器·kubernetes·networkpolicy
Crazy________4 小时前
Redis02:库切换、监控与安全配置
linux·redis·云原生·mybatis
lilian2335 小时前
Harmony os 技术实战|拼豆制图10:把取消、解析失败和保存失败写成可恢复状态机
java·javascript·华为·harmonyos