文章目录
- 前言
- 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.Meter是Micrometer指标体系最顶层根接口,用于表示一个具有名称和标签的度量值生产者。所有具体的Meter(Counter、Timer、Gauge等)都直接或间接实现此接口。
顶层接口方法:
publicinterfaceMeter{// 获取唯一标识IdgetId();// 获取瞬时测量值集合Iterable<Measurement>measure();}内部类:
| 内部类 | 作用 | 关键方法 |
|---|---|---|
Meter.Id | Meter 的唯一标识(名字+标签) | withName(),withTag(),withTags(),getTag(),getName(),getType(),getDescription() |
Meter.Builder | 构建并注册自定义 Meter | tags(),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 | 时间类瞬时值,单位秒 | ❌ | 存活时长、空闲时长 |
| StartTimeAwareMeter | OTLP内部扩展接口 | — | 框架内部,业务不直接使用 |
| CompositeMeter | 多注册器代理封装 | — | 框架内部,业务不直接使用 |
2. MeterRegistry 指标注册表
2.1 核心定义
io.micrometer.core.instrument.MeterRegistry是Micrometer指标体系抽象顶层注册器,作为指标工厂、指标容器、生命周期管理器。所有监控后端实现(Prometheus、OTLP、Simple等注册器)均继承该抽象类,统一指标创建、过滤、缓存、导出规范。
核心运行规则:
- 同一个映射后 Meter.Id在单个注册器内只会生成唯一
Meter; - 创建指标先执行全部
MeterFilter,支持改名、增删标签、拒绝指标; - 指标被过滤器拒绝或注册器关闭时,返回
NoopMeter空实现,避免空指针; - 删除主指标时,自动级联删除全部关联的合成衍生指标。
核心抽象工厂方法(由子类实现,产出对应后端专属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:简易内存注册表
SimpleMeterRegistry是Micrometer中最简单的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);}绑定功能实现逻辑(所有实现类统一规范):
- 内部预先定义好该组件需要采集的全部指标;
- 调用
registry.counter()/gauge()/timer()批量创建计量器; - 将计量器绑定到注册表,自动定时采集数据。
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 |
| JvmGcMetrics | GC 次数、耗时、各代晋升、GC 停顿分布 |
| JvmHeapPressureMetrics | 堆压力指标 |
| JvmThreadMetrics | 线程数(live/daemon/peak)、各状态线程数 |
| JvmThreadDeadlockMetrics | 死锁线程检测 |
| JvmCompilationMetrics | JIT 编译耗时 |
| JvmInfoMetrics | JVM 信息(版本等,作为 info gauge) |
| ExecutorServiceMetrics | 线程池运行状态 |
| DiskSpaceMetrics | 磁盘空间 |
比如,在JVM内存与缓冲池指标绑定器JvmMemoryMetrics中, 指标绑定执行流程:
- 方法接收
MeterRegistry,用于指标注册 - 获取全部
BufferPoolMXBean,遍历堆外缓冲区 - 组装标签,注册三类堆外缓冲区监控
Gauge指标 - 获取全部
MemoryPoolMXBean,遍历JVM内存池 - 根据指标规约,注册内存已使用、已提交、最大容量指标
- 所有
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.MeterFilter是Micrometer指标注册拦截过滤器接口,作为指标创建阶段的统一切面。所有指标注册到MeterRegistry时,按添加顺序串行经过全部过滤器,实现指标ID变换、准入控制、直方图配置修改能力。
核心运行规则:
- 过滤器仅在Meter首次注册阶段执行一次,指标后续
record()数据采集不会再次经过过滤器; - 执行顺序固定:
map()→accept()→configure(); - 任意过滤器返回
DENY,指标直接放弃注册,最终生成NoopMeter; NEUTRAL代表当前过滤器不决策,流转至下一个过滤器;ACCEPT强制放行指标;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.MeterFilterReply是MeterFilter准入判断的三态枚举,用来表达单个过滤器对指标是否注册的决策结果。多个过滤器串行执行时,依靠该枚举协同最终是否创建Meter。
枚举定义源码:
publicenumMeterFilterReply{/** * 强制放行指标,不受后续过滤器影响 */ACCEPT,/** * 拒绝注册指标,指标变成 NoopMeter,后续过滤器不再执行accept判断 */DENY,/** * 当前过滤器不做出决策,交给下一个过滤器继续判断 */NEUTRAL}决策组合逻辑表:
| 场景 | 最终结果 |
|---|---|
任意过滤器返回DENY | 拒绝注册 |
无DENY,至少一个ACCEPT | 允许注册 |
全部过滤器均返回NEUTRAL | 默认允许注册 |