Micrometer 系列【3】 指标核心组件:指标器、注册表、绑定器、过滤器
2026/7/24 19:16:09 网站建设 项目流程

文章目录

  • 前言
  • 1. Meter 指标器
    • 1.1 核心定义
    • 1.2 ID:指标唯一标识
    • 1.3 Measurement:度量值
    • 1.4 内置指标器类型
  • 2. MeterRegistry 指标注册表
    • 2.1 核心定义
    • 2.2 三类核心注册表
      • 2.2.1 SimpleMeterRegistry:简易内存注册表
      • 2.2.2 CompositeMeterRegistry:复合注册表
      • 2.2.3 Metrics.globalRegistry 全局静态注册表
  • 3. MeterBinder 指标绑定器
    • 3.1 核心定义
    • 3.2 内置实现
    • 3.3 JVM 监控相关
  • 4. MeterFilter 指标过滤器
    • 4.1 核心定义
    • 4.2 拦截策略 DENY / NEUTRAL / ACCEPT

前言

可观测性不是某个单一技术,而是一套方法论。它要求我们在应用代码中插入精心设计的埋点,持续采集反映系统内部运行状态的遥测数据,并把这些数据发送到统一的分析平台,从而打破系统的黑箱状态。

Micrometer提供了一套统一接口和机制,让开发者可以用一致的API采集业务指标和系统指标,然后无缝对接到任意后端监控系统。

本文将带你系统了解以下四大核心组件:

  • Meter计量器:计数器、仪表盘、计时器、分布摘要等核心类型的定义与适用场景
  • MeterRegistry注册表:指标的创建、托管、存储与导出机制,以及三种注册表的差异
  • MeterBinder绑定器:如何批量预置JVM、线程池、数据库连接池等内置指标
  • MeterFilter过滤器:指标统一治理,可实现:指标黑白名单、标签清洗、指标改名、动态统计配置。

1. Meter 指标器

1.1 核心定义

io.micrometer.core.instrument.MeterMicrometer指标体系最顶层根接口,用于表示一个具有名称和标签的度量值生产者。所有具体的MeterCounterTimerGauge等)都直接或间接实现此接口。

顶层接口方法:

publicinterfaceMeter{// 获取唯一标识IdgetId();// 获取瞬时测量值集合Iterable<Measurement>measure();}

内部类:

内部类作用关键方法
Meter.IdMeter 的唯一标识(名字+标签)withName(),withTag(),withTags(),getTag(),getName(),getType(),getDescription()
Meter.Builder构建并注册自定义 Metertags(),tag(),description(),baseUnit(),register()
Meter.MeterProvider<T>带标签模板的 Meter 注册辅助withTags(),withTag()

1.2 ID:指标唯一标识

内部类ID表示指标唯一标识,指标由【名称+标签】唯一确定,Id是不可变对象,所有withXXX方法返回全新实例。

classId{/** 指标名称 */privatefinalStringname;/** 维度标签集合,用于区分同名称下不同指标数据流 */privatefinalTagstags;/** 指标类型:COUNTER / GAUGE / TIMER 等 */privatefinalTypetype;/** * 衍生指标关联的原始指标Id(孵化API) * 直方图百分位、自动生成的直方图Gauge等合成指标会关联父指标 */privatefinalMeter.@NullableIdsyntheticAssociation;/** 指标描述信息,会同步推送到监控平台展示 */privatefinal@NullableStringdescription;/** 指标基础单位,例如 seconds、bytes、ms */privatefinal@NullableStringbaseUnit;@Incubating(since="1.1.0")Id(Stringname,Tagstags,@NullableStringbaseUnit,@NullableStringdescription,Typetype,Meter.@NullableIdsyntheticAssociation){this.name=name;this.tags=tags;this.baseUnit=baseUnit;this.description=description;this.type=type;this.syntheticAssociation=syntheticAssociation;}// ...省略剩余代码

1.3 Measurement:度量值

度量值/测量值Measurement)表示从指标器Meter采样得到的单次测量数据:

publicclassMeasurement{// 实时获取指标数值的函数(惰性求值)privatefinalDoubleSupplierf;// 当前测量值对应的统计类型privatefinalStatisticstatistic;/** * 构造 Measurement * @param valueFunction 获取数值的函数 * @param statistic 统计类型 * @since 1.10.0 */publicMeasurement(DoubleSuppliervalueFunction,Statisticstatistic){this.f=valueFunction;this.statistic=statistic;}publicMeasurement(Supplier<Double>valueFunction,Statisticstatistic){this.f=valueFunction::get;this.statistic=statistic;}/** * @return 获取本次测量的数值(调用时实时计算) */publicdoublegetValue(){returnf.getAsDouble();}/** * @return 当前测量对应的统计项类型 */publicStatisticgetStatistic(){returnstatistic;}}

Statistic枚举类用于描述一条Measurement数值代表什么统计含义:

publicenumStatistic{/** * 记录值总和(分布摘要使用) */TOTAL("total"),/** * 耗时总和(Timer 使用),以监控系统基础时间单位展示 */TOTAL_TIME("total"),/** * 调用次数 / QPS基数(计数器、计时器使用) */COUNT("count"),/** * 记录的最大值;代表时间时使用监控系统基础时间单位 */MAX("max"),/** * 瞬时值,仪表盘(Gauge)使用 */VALUE("value"),/** * 未确定类型 */UNKNOWN("unknown"),/** * 长任务计时器:当前正在运行的任务数量 */ACTIVE_TASKS("active"),/** * 长任务计时器:正在运行任务的持续时长 */DURATION("duration");// 导出为 tag 时对应的字符串值(statistic=xxx)privatefinalStringtagValueRepresentation;Statistic(StringtagValueRepresentation){this.tagValueRepresentation=tagValueRepresentation;}/** * 获取对外标签展示名称(用于生成 statistic 维度标签) */publicStringgetTagValueRepresentation(){returntagValueRepresentation;}}

1.4 内置指标器类型

io.micrometer.core.instrument.Meter顶层接口所有子类型继承关系树:

Micrometer内置全套指标器,适配全部业务监控场景:

指标类型核心特征是否维护内部状态使用场景
Counter单调递增事件次数请求数、异常数
Gauge瞬时可变快照队列长度、连接数
Timer已完成任务耗时分布接口耗时、DB耗时
DistributionSummary非时间数值分布报文大小、金额
LongTaskTimer观测运行中任务长任务并发监控
FunctionCounter读取外部递增值对接已有计数器
FunctionTimer读取外部次数+耗时外部现成计时指标
TimeGauge时间类瞬时值,单位秒存活时长、空闲时长
StartTimeAwareMeterOTLP内部扩展接口框架内部,业务不直接使用
CompositeMeter多注册器代理封装框架内部,业务不直接使用

2. MeterRegistry 指标注册表

2.1 核心定义

io.micrometer.core.instrument.MeterRegistryMicrometer指标体系抽象顶层注册器,作为指标工厂、指标容器、生命周期管理器。所有监控后端实现(PrometheusOTLPSimple等注册器)均继承该抽象类,统一指标创建、过滤、缓存、导出规范。

核心运行规则:

  1. 同一个映射后 Meter.Id在单个注册器内只会生成唯一Meter
  2. 创建指标先执行全部MeterFilter,支持改名、增删标签、拒绝指标;
  3. 指标被过滤器拒绝或注册器关闭时,返回NoopMeter空实现,避免空指针;
  4. 删除主指标时,自动级联删除全部关联的合成衍生指标。

核心抽象工厂方法(由子类实现,产出对应后端专属Meter实例):

publicabstractclassMeterRegistry{// 创建计数器protectedabstractCounternewCounter(Meter.Idid);// 创建瞬时仪表盘protectedabstract<T>GaugenewGauge(Meter.Idid,@NullableTobj,ToDoubleFunction<T>valueFunction);// 创建计时器protectedabstractTimernewTimer(Meter.Idid,DistributionStatisticConfigdistributionStatisticConfig,PauseDetectorpauseDetector);// 创建分布摘要protectedabstractDistributionSummarynewDistributionSummary(Meter.Idid,DistributionStatisticConfigdistributionStatisticConfig,doublescale);// 创建自定义MeterprotectedabstractMeternewMeter(Meter.Idid,Meter.Typetype,Iterable<Measurement>measurements);// 函数式计数器、函数式计时器等省略}

内部类:

内部类作用关键方法
MeterRegistry.Config注册器全局配置入口meterFilter(),commonTags(),namingConvention(),onMeterAdded(),pauseDetector()
MeterRegistry.More封装低频指标创建入口longTaskTimer(),counter(FunctionCounter),timer(FunctionTimer),timeGauge()

对外业务常用API

方法作用
counter(name, tags)创建/获取Counter
timer(name, tags)创建/获取Timer
summary(name, tags)创建/获取DistributionSummary
gauge(name, tags, obj, func)注册Gauge
find(name)/get(name)根据名称检索指标
remove(meter)/clear()动态移除指标
close()关闭注册器,释放资源

内部缓存容器说明:

容器用途
meterMap经过MeterFilter映射后的最终Id → Meter(对外生效指标缓存)
preFilterIdToMeterMap原始未过滤Id→ Meter
syntheticAssociations维护父指标与衍生合成指标关联(百分位、直方图Gauge)

2.2 三类核心注册表

micrometer-core包中MeterRegistry继承关系树:

2.2.1 SimpleMeterRegistry:简易内存注册表

SimpleMeterRegistryMicrometer中最简单的MeterRegistry实现,它不导出任何监控数据到外部后端(不会发到Prometheus/JMX/Datadog等任何监控系统),而是把所有Meter都保存在内存里。

适用场景:本地学习、单元测试、无监控平台时快速体验埋点。

简单示例:

MeterRegistryregistry=newSimpleMeterRegistry();

补充:Spring体系项目会自动装配SimpleMeterRegistry,无需手动new

2.2.2 CompositeMeterRegistry:复合注册表

支持挂载多个子注册表,实现一份指标同时上报多套监控系统

关键特性:

  • 未挂载任何子注册表时,埋点操作是空操作NOOP
  • 后续新增注册表,已创建的计量器会自动同步注册到新注册表。

简单示例:

// 创建复合注册表CompositeMeterRegistrycomposite=newCompositeMeterRegistry();Countercounter=composite.counter("visit.count");counter.increment();// 此时无挂载子注册表,空操作,数值始终0// 挂载内存注册表SimpleMeterRegistrysimple=newSimpleMeterRegistry();composite.add(simple);counter.increment();// 所有子注册表同步计数

2.2.3 Metrics.globalRegistry 全局静态注册表

Micrometer提供静态全局注册表Metrics.globalRegistry,底层本质就是CompositeMeterRegistry,配套静态方法快速创建计量器。

简单示例:

classDemo{// 推荐:缓存计量器,避免重复哈希查找privatestaticfinalCounterorderCounter=Metrics.counter("order.create","region","cn");voidcreateOrder(){orderCounter.increment();}// 标签依赖局部变量,只能方法内动态创建voidpay(StringpayType){Metrics.counter("order.pay","type",payType).increment();}}// 项目启动时绑定监控注册表classApplication{publicstaticvoidmain(String[]args){Metrics.addRegistry(newSimpleMeterRegistry());}}

3. MeterBinder 指标绑定器

3.1 核心定义

MeterBinder是一个统一接口,作用是批量预置埋点,把一类固定监控维度(JVM、线程池、Tomcat、磁盘、连接池等)的所有指标统一封装,只需调用一次bindTo(registry),就能一次性把整套计量器注册到注册表,不用开发者手动逐个创建Counter/Gauge/Timer

只有定义了接收一个MeterRegistry方法:

publicinterfaceMeterBinder{voidbindTo(MeterRegistryregistry);}

绑定功能实现逻辑(所有实现类统一规范):

  1. 内部预先定义好该组件需要采集的全部指标;
  2. 调用registry.counter()/gauge()/timer()批量创建计量器;
  3. 将计量器绑定到注册表,自动定时采集数据。

3.2 内置实现

io.micrometer.core.instrument.MeterRegistry继承关系树(截图没有截全):

Micrometer内置大量开箱即用的绑定器,全部实现MeterBinder,类别覆盖:

  • JVM监控(最常用)
  • 系统监控
  • 缓存框架
  • 日志框架
  • 数据库 /JPA
  • Web容器
  • 消息队列 /Kafka
  • HTTP客户端连接池
  • 网络 / 对象池

3.3 JVM 监控相关

JVM监控相关的类位于io.micrometer.core.instrument.binder.jvm包下,各自负责JVM的一个方面:

职责
ClassLoaderMetrics类加载/卸载计数、已加载类数
JvmMemoryMetrics各内存池/缓冲池的 used/committed/max
JvmGcMetricsGC 次数、耗时、各代晋升、GC 停顿分布
JvmHeapPressureMetrics堆压力指标
JvmThreadMetrics线程数(live/daemon/peak)、各状态线程数
JvmThreadDeadlockMetrics死锁线程检测
JvmCompilationMetricsJIT 编译耗时
JvmInfoMetricsJVM 信息(版本等,作为 info gauge)
ExecutorServiceMetrics线程池运行状态
DiskSpaceMetrics磁盘空间

比如,在JVM内存与缓冲池指标绑定器JvmMemoryMetrics中, 指标绑定执行流程:

  1. 方法接收MeterRegistry,用于指标注册
  2. 获取全部BufferPoolMXBean,遍历堆外缓冲区
  3. 组装标签,注册三类堆外缓冲区监控Gauge指标
  4. 获取全部MemoryPoolMXBean,遍历JVM内存池
  5. 根据指标规约,注册内存已使用、已提交、最大容量指标
  6. 所有JVM内存相关指标注册完毕,流程结束
/** * 将JVM缓冲区、内存池监控指标绑定注册到MeterRegistry * Micrometer指标采集入口,对外实现MeterBinder接口规范 * @param registry 指标注册器,用于注册各类Gauge监控指标 */@OverridepublicvoidbindTo(MeterRegistryregistry){// 遍历JVM缓冲区MXBean(Direct Buffer、Mapped Buffer等堆外缓冲区)for(BufferPoolMXBeanbufferPoolBean:ManagementFactory.getPlatformMXBeans(BufferPoolMXBean.class)){// 拼接自定义扩展标签 + buffer池名称id标签,区分不同缓冲区类型Iterable<Tag>tagsWithId=Tags.concat(extraTags,"id",bufferPoolBean.getName());// jvm.buffer.count:缓冲区池中缓冲区实例数量Gauge.builder("jvm.buffer.count",bufferPoolBean,BufferPoolMXBean::getCount).tags(tagsWithId).description("An estimate of the number of buffers in the pool").baseUnit(BaseUnits.BUFFERS).register(registry);// jvm.buffer.memory.used:缓冲区已占用内存大小(堆外内存)Gauge.builder("jvm.buffer.memory.used",bufferPoolBean,BufferPoolMXBean::getMemoryUsed).tags(tagsWithId).description("An estimate of the memory that the Java virtual machine is using for this buffer pool").baseUnit(BaseUnits.BYTES).register(registry);// jvm.buffer.total.capacity:缓冲区池总容量Gauge.builder("jvm.buffer.total.capacity",bufferPoolBean,BufferPoolMXBean::getTotalCapacity).tags(tagsWithId).description("An estimate of the total capacity of the buffers in this pool").baseUnit(BaseUnits.BYTES).register(registry);}// 遍历JVM内存池MXBean(Eden、Survivor、Old、Metaspace等内存分区)for(MemoryPoolMXBeanmemoryPoolBean:ManagementFactory.getPlatformMXBeans(MemoryPoolMXBean.class)){// 获取【内存已使用量】指标命名与标签约定MeterConvention<MemoryPoolMXBean>memoryUsedConvention=conventions.getMemoryUsedConvention();Gauge.builder(memoryUsedConvention.getName(),memoryPoolBean,(mem)->getUsageValue(mem,MemoryUsage::getUsed)).tags(memoryUsedConvention.getTags(memoryPoolBean)).description("The amount of used memory").baseUnit(BaseUnits.BYTES).register(registry);// 获取【已提交内存】指标命名与标签约定(OS已分配给JVM的内存)MeterConvention<MemoryPoolMXBean>memoryCommittedConvention=conventions.getMemoryCommittedConvention();Gauge.builder(memoryCommittedConvention.getName(),memoryPoolBean,(mem)->getUsageValue(mem,MemoryUsage::getCommitted)).tags(memoryCommittedConvention.getTags(memoryPoolBean)).description("The amount of memory in bytes that is committed for the Java virtual machine to use").baseUnit(BaseUnits.BYTES).register(registry);// 获取【最大可用内存】指标命名与标签约定(Xmx/MetaspaceMax上限)MeterConvention<MemoryPoolMXBean>memoryMaxConvention=conventions.getMemoryMaxConvention();Gauge.builder(memoryMaxConvention.getName(),memoryPoolBean,(mem)->getUsageValue(mem,MemoryUsage::getMax)).tags(memoryMaxConvention.getTags(memoryPoolBean)).description("The maximum amount of memory in bytes that can be used for memory management").baseUnit(BaseUnits.BYTES).register(registry);}}

4. MeterFilter 指标过滤器

4.1 核心定义

io.micrometer.core.instrument.config.MeterFilterMicrometer指标注册拦截过滤器接口,作为指标创建阶段的统一切面。所有指标注册到MeterRegistry时,按添加顺序串行经过全部过滤器,实现指标ID变换、准入控制、直方图配置修改能力。

核心运行规则:

  1. 过滤器仅在Meter首次注册阶段执行一次,指标后续record()数据采集不会再次经过过滤器;
  2. 执行顺序固定:map()accept()configure()
  3. 任意过滤器返回DENY,指标直接放弃注册,最终生成NoopMeter
  4. NEUTRAL代表当前过滤器不决策,流转至下一个过滤器;ACCEPT强制放行指标;
  5. map()转换后的Meter.Id作为后续所有流程的唯一标识。

核心默认回调方法(接口默认实现):

publicinterfaceMeterFilter{// 1. 修改Meter.Id:指标名称、标签增删、标签键/值重映射defaultMeter.Idmap(Meter.Idid){returnid;}// 2. 指标准入判定:ACCEPT / DENY / NEUTRALdefaultMeterFilterReplyaccept(Meter.Idid){returnMeterFilterReply.NEUTRAL;}// 3. 仅对Timer、DistributionSummary生效,修改分布统计、直方图配置default@NullableDistributionStatisticConfigconfigure(Meter.Idid,DistributionStatisticConfigconfig){returnconfig;}}

静态工厂方法分类:

方法分组代表方法作用阶段
标签操作commonTags()ignoreTags()renameTag()replaceTagValues()map()
指标黑白名单deny()denyUnless()accept()denyNameStartsWith()accept()
高基数限流防护maximumAllowableMetrics()maximumAllowableTags()accept()
分布统计配置maxExpected()minExpected()configure()
条件包装器forMeters()组合任意过滤器

对外业务常用能力说明:

方法典型业务用途
commonTags()全局统一追加环境、应用等公共标签
ignoreTags()删除高基数标签(uri、traceId、userId),控制时序爆炸
replaceTagValues()收敛标签取值,路径归一化/user/123 → /user/{id}
denyNameStartsWith()批量屏蔽内部调试、无用监控指标
maximumAllowableTags()限制单个tagKey最大取值数量,做高基数熔断
maxExpected/minExpected调整Timer直方图区间,减少Prometheus存储开销

4.2 拦截策略 DENY / NEUTRAL / ACCEPT

io.micrometer.core.instrument.config.MeterFilterReplyMeterFilter准入判断的三态枚举,用来表达单个过滤器对指标是否注册的决策结果。多个过滤器串行执行时,依靠该枚举协同最终是否创建Meter

枚举定义源码:

publicenumMeterFilterReply{/** * 强制放行指标,不受后续过滤器影响 */ACCEPT,/** * 拒绝注册指标,指标变成 NoopMeter,后续过滤器不再执行accept判断 */DENY,/** * 当前过滤器不做出决策,交给下一个过滤器继续判断 */NEUTRAL}

决策组合逻辑表:

场景最终结果
任意过滤器返回DENY拒绝注册
DENY,至少一个ACCEPT允许注册
全部过滤器均返回NEUTRAL默认允许注册

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询