Micrometer 系列【6】Gauge 瞬时仪表盘

Micrometer 系列【6】Gauge 瞬时仪表盘 文章目录1. 概述1.1 核心概念1.2 类图1.2.1 顶层接口1.2.2 普通 Gauge 实现类1.2.3 TimeGauge 时间专用实现类2. 使用示例2.1 构建方式2.1.1 使用 MeterRegistry 构建2.2.2 使用流式 Builder 构建2.2 构建参数2.3 可变数字仪表盘2.4 TimeGauge 时间专用仪表盘2.5 MultiGauge 动态多维度仪表盘2.6 JvmMemoryMetrics JVM 内存指标3. 核心总结与避坑规范3.1 常见故障Gauge 上报 NaN / 指标消失3.2 生产环境避坑清单3.3 关键概念速记1. 概述1.1 核心概念Gauge是用于获取当前瞬时数值的计量器。典型使用场景集合或Map的元素数量、运行状态线程总数。设计思路Gauge采用采样读取模式而非主动更新模式两次采样间隔内发生的所有数值变更都不会留存记录。当指标上报至监控后端时仅会上报采样那一刻的瞬时值中间所有临时修改值都会丢失因此频繁更新中间数值没有任何意义。JVM Gauge示例Gauge适合监控带有天然上限的指标。不建议使用Gauge监控请求量这类无上限持续增长的数据这类指标在应用整个生命周期只会不断上涨。凡是可以用Counter统计的场景永远不要使用GaugeGauge与Counter核心区分计量器数据模型更新逻辑适用场景Gauge瞬时快照值可升可降定时采样读取外部对象无累加在线用户、队列堆积、连接池、活跃线程Counter累计总和只增不减业务主动调用 increment()请求次数、异常总数、业务事件1.2 类图io.micrometer.core.instrument.Gauge继承树1.2.1 顶层接口Gauge是所有瞬时仪表盘指标的统一父接口定义核心方法measure()监控拉取指标时采样读取瞬时值无increment()只负责观测外部对象快照海森堡仪表盘核心规范TimeGauge继承/衍生自Gauge的专用子接口专门用于时间维度瞬时指标内置时间单位转换逻辑上报时自动换算为监控后端标准时间Prometheus统一转秒如毫秒、微秒时间指标埋点。1.2.2 普通 Gauge 实现类DefaultGauge最常用默认实现我们日常SimpleMeterRegistry、SpringBoot默认注册表底层用它支持弱/强引用观测对象、函数式取值JVM内存、线程、队列长度埋点底层都是DefaultGauge。DropwizardGauge兼容Dropwizard监控体系的适配包装类当项目引入micrometer-registry-dropwizard注册表时自动生成该实例对接Dropwizard指标格式。NoopGauge空操作兜底实现当应用禁用指标、无可用MeterRegistry时返回measure()返回空集合不采集任何指标无性能损耗和之前的NoopCounter设计一致。CompositeGauge对应CompositeMeterRegistry多注册表聚合容器同时对接Prometheus、Datadog多个监控后端时外层包装CompositeGauge内部持有多个真实Gauge一次采样同步推送到所有注册表。1.2.3 TimeGauge 时间专用实现类NoopTimeGauge时间指标专属空实现禁用监控时兜底不产生任何指标输出。CompositeTimeGauge复合注册表专用时间仪表盘多监控后端聚合场景使用和CompositeGauge逻辑一致仅针对TimeGauge接口。两个Anonymous in MeterRegistry匿名内部实现分别对应绑定AtomicInteger/AtomicLong可变数字的GaugeMultiGauge多动态维度仪表盘SQL分组多标签指标2. 使用示例2.1 构建方式2.1.1 使用 MeterRegistry 构建MeterRegistry提供多种构造Gauge的重载方法Contract(_, _, null, _ - null; _, _, !null, _ - !null)publicTNullableTgauge(Stringname,IterableTagtags,NullableTstateObject,ToDoubleFunctionTvalueFunction){Gauge.builder(name,stateObject,valueFunction).tags(tags).register(this);returnstateObject;}TGaugegauge(Meter.Idid,NullableTobj,ToDoubleFunctionTvalueFunction){returnregisterMeterIfNecessary(Gauge.class,id,(registry,mappedId)-registry.newGauge(mappedId,obj,valueFunction),NoopGauge::new);}//............ 省略其他支持观测普通对象、数值函数、集合、Map// 通用函数式Gauge观测List长度ListStringlistregistry.gauge(listGauge,Collections.emptyList(),newArrayList(),List::size);// 简化API专门监控集合容量ListStringlist2registry.gaugeCollectionSize(listSize2,Tags.empty(),newArrayList());// 监控Map键值对数量MapString,Integermapregistry.gaugeMapSize(mapGauge,Tags.empty(),newHashMap());所有Gauge创建方法对被观测对象仅持有弱引用不会阻止对象垃圾回收。2.2.2 使用流式 Builder 构建框架提供流式构造器快速创建Gauge支持自定义描述、标签GaugegaugeGauge.builder(gauge,myObj,myObj::gaugeValue).description(a description of what this gauge does)// 可选指标描述.tags(region,test)// 可选自定义维度标签.register(registry);常规业务开发几乎不需要持有返回的Gauge实例仅单元测试场景会使用注册后Gauge会自动定时采样读取数值。2.2 构建参数构造时必填固定参数无默认创建时必须传入全程不可修改name指标名称无默认值必填obj被观测业务对象允许传 null无默认fToDoubleFunction采样取值函数无默认必填内置默认值参数成员字段默认值含义说明tagsTags.empty()空标签集合无任何维度标签strongReferencefalse默认使用弱引用持有观测对象对象无外部强引用会被GC指标变为NaNsyntheticAssociationnull无衍生指标关联普通业务Gauge非Histogram分位数等派生指标descriptionnull无指标描述文本监控平台不展示说明baseUnitnull无计量单位如threads、MB、tasks等2.3 可变数字仪表盘Gauge支持绑定可修改数字子类用于手动维护指标值例如java.util.concurrent.atomic包下AtomicInteger、AtomicLong以及Guava提供的AtomicDouble// 创建可变数值对象并注册GaugeAtomicIntegermyGaugeregistry.gauge(numberGauge,newAtomicInteger(0));// 业务代码任意位置修改数值myGauge.set(27);myGauge.set(11);该用法与其他计量器存在明显区别创建时返回的不是Gauge实例而是被观测的可变数值对象。遵循「海森堡仪表盘」设计思想Gauge注册完成后自动工作业务代码无需直接操作Gauge对象。框架仅返回被埋点的数值对象一行代码完成对象创建与指标注册。重点注意频繁调用set()修改数值没有业务价值两次上报之间的中间值全部丢弃监控系统只会接收采样瞬间的数值。禁止使用基础数字类型int、long或不可变包装类Integer、Long创建Gauge。此类数值无法修改Gauge数值将永久不变。同名同标签组合仅能注册一个Gauge重复注册不会生效同时输出警告日志WARNING:ThisGaugehas been already registered(MeterId{namemy.gauge,tags[]}),the registration will beignored.Notethat subsequent logs will be logged at debug level.该场景使用频率低于函数回调式Gauge。2.4 TimeGauge 时间专用仪表盘TimeGauge是Gauge的特化实现专门用于统计时间类指标可根据不同监控后端自动转换为标准时间单位。创建时指定原始时间单位TimeUnitAtomicIntegermsTimeGaugenewAtomicInteger(4000);AtomicIntegerusTimeGaugenewAtomicInteger(4000);// 毫秒时间指标TimeGauge.builder(my.gauge,msTimeGauge,TimeUnit.MILLISECONDS,AtomicInteger::get).register(registry);// 微秒时间指标TimeGauge.builder(my.other.gauge,usTimeGauge,TimeUnit.MICROSECONDS,AtomicInteger::get).register(registry);以Prometheus为例所有时间指标会统一转换为秒输出# HELP my_gauge_seconds # TYPE my_gauge_seconds gauge my_gauge_seconds 4.0 # HELP my_other_gauge_seconds # TYPE my_other_gauge_seconds gauge my_other_gauge_seconds 0.0042.5 MultiGauge 动态多维度仪表盘Micrometer提供特殊Gauge实现MultiGauge用于管理动态增减的多维度统计指标。适用于SQL分组查询等场景每组标签对应一条独立指标。示例统计任务不同状态下的组件数量// 对应SQLSELECT count(*) from job group by status WHERE job dirtyMultiGaugestatusesMultiGauge.builder(statuses).tag(job,dirty).description(The number of widgets in various statuses).baseUnit(widgets).register(registry);// 每次刷新查询结果后执行statuses.register(resultSet.stream().map(result-Row.of(Tags.of(status,result.getAsString(status)),result.getAsInt(count))).collect(toList()),true// overwrite 覆盖标记);overwrite参数说明overwritetrue每次注册代表全新快照旧标签行全部替换为最新数值overwritefalse已有标签行数值保留仅新增不存在的标签新快照中不存在的标签行会被自动删除。2.6 JvmMemoryMetrics JVM 内存指标在JvmMemoryMetrics#bindTo()方法中会读取JVM MXBean批量注册一批Gauge指标。代码分成两大块第一循环BufferPoolMXBean→ 堆外缓冲区Direct Buffer/Mapped Buffer指标第二循环MemoryPoolMXBean→ 内存池指标Eden、Survivor、Old、Metaspace等BufferPoolMXBean、MemoryPoolMXBean都是JDK原生自带的管理接口由JVM虚拟机实现提供属于Java Management ExtensionsJMX规范。随后会进行Gauge自动注册比如缓冲池中缓冲区数量jvm.buffer.count指标对象实例如下全部都是Gauge瞬时指标不会主动采集Prometheus/ 监控系统主动拉取的一瞬间才执行回调函数去查JVM状态。3. 核心总结与避坑规范3.1 常见故障Gauge 上报 NaN / 指标消失开发者必须对被Gauge观测的业务对象保留强引用。Micrometer仅对观测对象持有弱引用一旦对象无外部强引用并被GC回收采样时Gauge会返回NaN或直接无指标输出具体行为由不同MeterRegistry实现决定。如果Gauge启动初期正常展示一段时间后突然消失、数值变为NaN基本可以判定被观测底层对象已被垃圾回收。3.2 生产环境避坑清单禁止用Gauge统计持续增长事件优先使用Counter观测集合、自定义对象时代码中必须保留对象强引用防止GC导致指标NaNAtomic可变数值模式下频繁set无意义监控仅采集采样点数值不可变数字Integer、基础类型无法构建有效Gauge同名同标签Gauge仅能注册一次重复注册会被忽略并打印警告MultiGauge刷新时合理配置overwrite控制过期维度自动清理。3.3 关键概念速记海森堡仪表盘仅采样读取时获取数值中间变更全部丢失弱引用机制Micrometer不持有观测对象强引用开发者自行管理生命周期TimeGauge自动适配各监控后端时间单位统一标准化输出MultiGauge支持动态多标签分组指标适配数据库分组统计场景。