Micrometer 系列【35】Spring Boot Micrometer Metrics 数据采集、指标查询解析
文章目录1. 前言2. 指标查询2.1 MetricsEndpointActuator 指标端点2.1.1 核心成员2.1.2 listNames()全量指标名称查询2.1.3 metric() 单指标详情查询2.2 全量指标名称查询接口2.3 指标详情接口3. 数据采集3.1 采集时触发3.2 内部状态维护3.2.1 自动配置3.2.2 请求开始3.2.3 请求结束3.2.4 查询数据1. 前言在之前的几篇文档中我们已经了解了在Micrometer原生框架中指标的整个生命周期包括实例化、注册、采集、销毁。原生MeterRegistry提供register、remove、close等底层API手动完成指标的增删与资源回收。那么在Spring Boot环境下一切不完全等同原生编码。Spring Boot自动装配、自动注入MeterRegistry、MeterBinder、自动构建内置指标、上下文生命周期钩子已经做到了开箱即用。通过之前Spring Boot Micrometer Metrics自动配置章节我们已经了解到根据classpath上的依赖创建对应的MeterRegistryBean整个Spring 应用只存在一个MeterRegistry实例。业务代码直接Autowired MeterRegistry registry注入使用。MeterBinder自动绑定内置指标Spring容器启动完成后收集全部MeterBinderBean并自动注册到全局registry。那么在Spring Boot场景中是如何实现数据采集、指标查询的呢2. 指标查询2.1 MetricsEndpointActuator 指标端点核心作用提供Spring Boot Actuator内置/actuator/metrics接口用于人工调试查看应用所有指标、实时数值、标签信息仅做只读查询不参与指标采集与生命周期管理。支持接口GET /actuator/metrics获取应用所有已注册指标名称列表GET /actuator/metrics/{metricName}?tagk1:v1根据指标名 标签过滤查询指标实时测量值、可用标签2.1.1 核心成员端点核心依赖MeterRegistrySpring Boot自动注入组合注册中心Endpoint(idmetrics)publicclassMetricsEndpoint{privatefinalMeterRegistryregistry;// ...}在引入了micrometer-registry-prometheus后注入的是PrometheusMeterRegistrycollectNames(names, this.registry)递归收集指标名privatevoidcollectNames(SetStringnames,MeterRegistryregistry){if(registryinstanceofCompositeMeterRegistrycompositeMeterRegistry){// 复合注册中心本身不存储meter遍历全部子registry递归调用compositeMeterRegistry.getRegistries().forEach((member)-collectNames(names,member));}else{// 真实底层registry(Prometheus/SimpleMeterRegistry)// getMeters() 获取meterMap全部meter快照registry.getMeters().stream().map(this::getName)// meter.getId().getName()只拿指标名字丢弃tag.forEach(names::add);}}2.1.2 listNames()全量指标名称查询核心逻辑递归遍历MeterRegistry所有指标器收集所有Meter的名称去重排序。关键特性仅读取指标元数据名称不执行measure()、不采集实时数值性能极高。ReadOperationpublicMetricNamesDescriptorlistNames(){SetStringnamesnewTreeSet();collectNames(names,this.registry);returnnewMetricNamesDescriptor(names);}2.1.3 metric() 单指标详情查询完整执行步骤解析标签参数parseTags()解析URLtagkey:value参数格式错误抛出参数异常匹配指标实例findFirstMatchingMeters()根据指标名标签匹配容器内注册的Meter空值处理无匹配指标直接返回nullHTTP响应404采集合并数值getSamples()调用meter.measure()获取测量数据合并多实例指标值过滤可用标签getAvailableTags()收集剩余可过滤标签移除 URL 已传入的过滤标签组装返回数据封装指标名、单位、测量值、可用标签等信息。ReadOperationpublicNullableMetricDescriptormetric(SelectorStringrequiredMetricName,NullableListStringtag){ListTagtagsparseTags(tag);CollectionMetermetersfindFirstMatchingMeters(this.registry,requiredMetricName,tags);if(meters.isEmpty()){returnnull;}MapStatistic,DoublesamplesgetSamples(meters);MapString,SetStringavailableTagsgetAvailableTags(meters);tags.forEach((t)-availableTags.remove(t.getKey()));Meter.IdmeterIdmeters.iterator().next().getId();returnnewMetricDescriptor(requiredMetricName,meterId.getDescription(),meterId.getBaseUnit(),asList(samples,Sample::new),asList(availableTags,AvailableTag::new));}解析tag参数parseTags(tag)将url的tagkey:value字符串列表转换MicrometerTag对象列表格式错误抛出异常。没有传tag返回空集合。findFirstMatchingMeters()在MeterRegistry查找匹配meterprivateCollectionMeterfindFirstMatchingMeters(MeterRegistryregistry,Stringname,IterableTagtags){if(registryinstanceofCompositeMeterRegistrycompositeMeterRegistry){returnfindFirstMatchingMeters(compositeMeterRegistry,name,tags);}returnregistry.find(name).tags(tags).meters();}getSamples()mergeMeasurements()采集并合并测量值meters.forEach((meter)-mergeMeasurements(samples,meter));meter.measure()// 实时执行采集返回一组Measurement{Statistic, value}合并规则Statistic.MAX取各个meter的最大值Double::max其余所有统计项执行求和Double::sum注意不是求平均值是sum/max合并。getAvailableTags()收集可用tag集合已经在url请求参数中指定的tag key从availableTags移除代表该维度已经被过滤不能再下钻。tags.forEach((t)-availableTags.remove(t.getKey()));最后组装MetricDescriptor DTO返回。2.2 全量指标名称查询接口需要开启暴露metrics端点management.endpoints.web.exposure.includemetrics访问路径GET /actuator/metrics请求参数无任何请求参数不需要path变量、不需要query参数。HTTP MethodGET对应注解ReadOperation只读操作。对比GET /actuator/metrics/{metricName}是另外一个方法metric()带Selector路径变量。响应DTOMetricNamesDescriptor implements OperationResponseBody响应示例{names:[application.ready.time,http.server.requests,jvm.memory.used,jvm.threads.live,system.cpu.usage]}2.3 指标详情接口访问路径GET /actuator/metrics/{metricName}请求参数路径变量参数说明metricName必填指标名称如jvm.memory.usedQuery参数参数说明示例tag可选可多次传用于按tag过滤meter格式必须key:valuetagstatus:200tagmethod:GET请求示例1不带tag过滤GET /actuator/metrics/jvm.memory.used请求示例2带tag过滤GET /actuator/metrics/http.server.requests?tagstatus:200tagmethod:GET返回DTOMetricDescriptor implements OperationResponseBody响应示例{name:jvm.memory.used,description:The amount of used memory,baseUnit:bytes,measurements:[{statistic:VALUE,value:186421344}],availableTags:[{tag:area,values:[heap,nonheap]}]}字段说明字段说明name指标名称入参传入的metricNamedescription指标描述来自MeterId可为nullbaseUnit基础单位(bytes/seconds等)可为nullmeasurements采集统计结果数组‑statistic统计类型VALUE/COUNT/TOTAL_TIME/MAX等‑value采集出来的数值实时调用meter.measure()得到availableTags还可以继续筛选的tag维度url上已经指定过的tag会被剔除‑tagtag key‑values该key下全部可选tag值集合3. 数据采集3.1 采集时触发我们以/actuator/metrics/jvm.memory.used为例MetricsEndpoint#findFirstMatchingMeters匹配到6个Gauge实例调用meter.measure()获取每个指标器中的所有度量值Measurement调用JDK Map原生merge方法如果key不存在直接put (key,value)如果key已经存在执行第三个参数的合并函数旧值、新值传入函数函数返回值作为新的value存入map。调用measurement.getValue()时触发真正采集指标数值samples.merge(measurement.getStatistic(),measurement.getValue()jvm.memory.used的Statistic类型是Value则会对所有Gauge实例的值进行求和返回进行标签过滤后直接返回响应对象使用Actuator内部的JSon序列化后返回到浏览器{availableTags:[{tag:area,values:[heap,nonheap]},{tag:id,values:[G1 Survivor Space,Compressed Class Space,Metaspace,CodeCache,G1 Old Gen,G1 Eden Space]}],baseUnit:bytes,description:The amount of used memory,measurements:[{statistic:VALUE,value:109319464}],name:jvm.memory.used}3.2 内部状态维护类型Timer计时器指标记录http服务请求耗时、请求次数。http.server.requests指标在Spring Boot 3.x以上的版本都基于Micrometer Observation模式进行统一观测了这里我们简单了解下。3.2.1 自动配置WebMvcObservationAutoConfiguration自动注册过滤器ServerHttpObservationFilter进行观测埋点。DefaultMeterObservationHandler把Observation事件转成Micrometer Meter指标3.2.2 请求开始执行时机HTTP请求进入过滤器业务逻辑尚未执行请求开始时执行DefaultMeterObservationHandler#onStart方法若开启shouldCreateLongTaskTimer获取/创建{name}.active的LongTaskTimer调用.start()得到采样对象将LongTaskTimer.Sample存入Observation.Context调用Timer.start(meterRegistry)生成Timer.Sample仅记录开始时间戳将Timer.Sample存入上下文OverridepublicvoidonStart(Observation.Contextcontext){if(shouldCreateLongTaskTimer){LongTaskTimer.SamplelongTaskSamplemeterRegistry.more().longTaskTimer(context.getName().active,createTags(context)).start();context.put(LongTaskTimer.Sample.class,longTaskSample);}Timer.SamplesampleTimer.start(meterRegistry);context.put(Timer.Sample.class,sample);}3.2.3 请求结束执行时机请求完成正常返回 / 异常请求完成时执行DefaultMeterObservationHandler#onStop方法从上下文提取、组装tags追加error标签获取上下文保存的Timer.SamplemeterRegistry.timer(context.getName(), tags)nametags不存在新建Timer Meter注册进meterMap已存在直接复用已有Timersample.stop(timer)计算耗时执行timer.record()写入耗时统计若开启长任务计时器取出LongTaskTimer.Sample执行.stop()结束活跃任务统计Override// TODO decide what to do about context.getName being NullableSuppressWarnings(NullAway)publicvoidonStop(Observation.Contextcontext){ListTagtagscreateTags(context);tags.add(Tag.of(error,getErrorValue(context)));Timer.Samplesamplecontext.getRequired(Timer.Sample.class);sample.stop(this.meterRegistry.timer(context.getName(),tags));if(shouldCreateLongTaskTimer){LongTaskTimer.SamplelongTaskSamplecontext.getRequired(LongTaskTimer.Sample.class);longTaskSample.stop();}}3.2.4 查询数据访问actuator/metrics/http.server.requests和【采集时触发】流程一致唯一的区别就是数据已经在内存中了。返回示例{availableTags:[{tag:exception,values:[none,IOException]},{tag:method,values:[GET]},{tag:error,values:[none,IOException]},{tag:uri,values:[/actuator/metrics/{requiredMetricName},/actuator/metrics,/actuator/prometheus]},{tag:outcome,values:[SUCCESS]},{tag:status,values:[200]}],baseUnit:seconds,measurements:[{statistic:COUNT,value:291},{statistic:TOTAL_TIME,value:8785.79363672},{statistic:MAX,value:714.0757704}],name:http.server.requests

相关新闻

GitHub Copilot 用户破 2000 万!AI 编程已不是「尝鲜」,掌握这 7 步让你的项目不再返工(收藏版)

GitHub Copilot 用户破 2000 万!AI 编程已不是「尝鲜」,掌握这 7 步让你的项目不再返工(收藏版)

GitHub Copilot 用户量激增,AI 工具普及但项目失败率居高不下。多数人仅将 AI 视为代码生成工具,忽略了项目成功的关键环节。文章提出 AI 辅助项目的 7 个核心环节:立项论证、需求定义、方案设计、辅助开发、测试验收、发布上线、运营迭代。强…

2026/10/7 22:46:34 阅读更多 →
Windows版本升级终极指南:如何免费实现版本自由切换的完整教程

Windows版本升级终极指南:如何免费实现版本自由切换的完整教程

Windows版本升级终极指南:如何免费实现版本自由切换的完整教程 【免费下载链接】In-Place_Upgrade_Helper Helper-Tool for Windows 10/11/Server In-Place-upgrades and changing between Windows Editions 项目地址: https://gitcode.com/GitHub_Trending/in/In…

2026/10/3 19:17:15 阅读更多 →
小白程序员必看:大模型检索与排序实战指南(收藏版)

小白程序员必看:大模型检索与排序实战指南(收藏版)

本文详细介绍了多轮Query改写、全文检索、向量检索、混合检索、排序算法以及基于LLM的相关性判断等大模型检索与排序的关键技术。内容涵盖了从基础概念到实际应用的多个方面,包括TFIDF、BM25、BERT、向量模型、RRF融合排序、Cross-Encoder、CoIBERT等,并…

2026/10/3 11:43:13 阅读更多 →

最新新闻

Doris JSON数组解析优化:从正则切分到JSONB+EXPLODE的实践

Doris JSON数组解析优化:从正则切分到JSONB+EXPLODE的实践

1. 这个版本优化到底解决了什么问题1.1 日志场景里的 JSON 数组字段做数仓的人大概率都有过这种经历:上游埋点日志为了省事,把一个事件的所有上下文全塞进一个大 JSON 字段里,其中一定会有个数组字段,比如用户浏览商品列表、曝光位…

2026/10/12 5:54:29 阅读更多 →
no_DS.zip:macOS 隐藏文件清理与跨平台交付包净化指南

no_DS.zip:macOS 隐藏文件清理与跨平台交付包净化指南

简介:一个仅763B的ZIP压缩包,内含run.bat与no-ds.js两个文件,面向对Windows批处理与JavaScript脚本协同工作感兴趣的开发者和技术爱好者。ZIP格式使这个小巧的脚本组合便于存储和传播,压缩包共2个文件,其中run.bat为批…

2026/10/12 5:54:29 阅读更多 →
Flutter开发实战:从Widget布局到状态管理与打包

Flutter开发实战:从Widget布局到状态管理与打包

1. 先说说我为什么盯着 Flutter 不放在移动开发这行做了快十年,我先后用过原生、跨平台方案,也带过几个项目从零搭架构。这期间最让我头疼的不是写业务逻辑,而是“一套代码交付多个平台”这件事:iOS 和 Android 的定制逻辑、多端对…

2026/10/12 5:54:29 阅读更多 →
在 Xcode 里使用 Cursor 编程:把 Base URL 改到 TaoToken 的配置与验证

在 Xcode 里使用 Cursor 编程:把 Base URL 改到 TaoToken 的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 5:54:29 阅读更多 →
私有IP与公有IP详解:从RFC1918到NAT端口映射,彻底搞懂内网外网通信

私有IP与公有IP详解:从RFC1918到NAT端口映射,彻底搞懂内网外网通信

聊到 IP 地址,很多刚入门的朋友第一件事就是打开浏览器去搜“我的 IP 是多少”,然后看到一串类似58.xxx.xxx.xxx的数字,又去路由器后台看一眼,发现 WAN 口和 LAN 口的地址完全不一样,甚至和自己电脑上ipconfig出来的结…

2026/10/12 5:54:29 阅读更多 →
从Shape案例彻底搞懂Java抽象类设计与落地细节

从Shape案例彻底搞懂Java抽象类设计与落地细节

写这篇的时候,我心里其实挺有感触的。很多人学抽象类,都是从 Shape 这个类入手的——课本上画个圆、画个矩形,定义个area()方法,然后就没下文了。结果到了实际项目里,一用就懵:抽象类到底该放哪些方法&…

2026/10/12 5:53:28 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →