Micrometer 系列【2】可观测性:指标(Metrics)

文章目录

  • [1. 基础概念](#1. 基础概念)
    • [1.1 可观测性](#1.1 可观测性)
    • [1.2 指标定义](#1.2 指标定义)
    • [1.3 数据结构](#1.3 数据结构)
  • [2. 指标名称(Metric Name)](#2. 指标名称(Metric Name))
    • [2.1 核心概念](#2.1 核心概念)
    • [2.2 指标命名规范](#2.2 指标命名规范)
    • [2.3 跨平台兼容](#2.3 跨平台兼容)
  • [3. 标签(Tag)](#3. 标签(Tag))
    • [3.1 核心概念](#3.1 核心概念)
    • [3.2 标签编写规范](#3.2 标签编写规范)
      • [3.2.1 ✅ 推荐规范写法](#3.2.1 ✅ 推荐规范写法)
      • [3.2.2 ❌ 禁止反模式](#3.2.2 ❌ 禁止反模式)
    • [3.3 全局公共标签](#3.3 全局公共标签)
      • [3.3.1 核心概念](#3.3.1 核心概念)
      • [3.3.2 两种配置方式](#3.3.2 两种配置方式)
      • [3.3.3 最佳实践](#3.3.3 最佳实践)
    • [3.4 低基数标签](#3.4 低基数标签)
    • [3.5 高基数标签](#3.5 高基数标签)
  • [4. 指标数值(Value)](#4. 指标数值(Value))
  • [5. 时间戳(Timestamp)](#5. 时间戳(Timestamp))

1. 基础概念

1.1 可观测性

云原生计算基金会(CNCF)行业标准定义:可观测性,指仅依靠系统对外输出的外部信号,推断系统内部运行状态的能力

可观测性Observability) 是衡量复杂分布式系统的核心属性:系统通过向外持续产出遥测数据(指标、链路追踪、日志),使开发运维人员仅凭外部信号,反向推导系统内部运行状态。

区别于传统监控只能识别预先定义的已知问题,可观测性体系擅长排查从未遇到的未知异常,是微服务、云原生系统稳定性建设的基础能力。

可观测性三大支柱

  • 指标 Metrics:宏观聚合数据(QPS、耗时、错误率、队列堆积),用于告警、大盘、趋势分析;
  • 分布式追踪 Tracing:单次请求完整链路,用于定位慢调用、依赖故障;
  • 日志 Logging:详细业务上下文,用于精准问题定位。

Spring 云原生生态中,Micrometer 整套组件正是构建 JVM 应用可观测能力的标准化基础设施。

1.2 指标定义

指标Metrics)是可观测性三大支柱 之一,是对系统、业务运行状态的数值型时序采集数据

核心特征

  1. 定时采样,存储为时序数据;
  2. 纯数字,支持聚合、计算、告警、可视化;
  3. 性能开销极低,适合长期存储大盘数据;
  4. 回答量化问题:QPS、延迟、错误率、资源占用、业务吞吐量。

例如,JVM 堆内存指标Java 应用健康度的核心生命线指标 ,既能提前预警内存泄漏、OOM 宕机,又能指导 GC 调优、容量规划,故障时快速缩小排查范围,是线上服务稳定运行必不可少的监控依据。

系统每秒定时采集 JVM 堆内存,每一条采集数据都会附带时间戳,时序库按时间串联成折线图,直观看到内存变化趋势。

折线图示例:

1.3 数据结构

一条 Metrics 时序样本由 5 部分核心字段 组成:

text 复制代码
<指标名称> {<标签Key=Value列表>} <指标数值> <时间戳> [元数据类型]

字段说明:

  • name:指标名称,标识指标语义,区分指标大类,遵循命名规范;
  • labels/tag:标签,用于维度细分的键值对结构;
  • value:指标数值,表示当前指标的具体数值;
  • timestamp:时间戳,标记该数值采集的精确时刻,构成时序数据核心;
  • metric type:指标类型元数据,属于指标全局元数据,定义数值业务逻辑。

Prometheus 原生格式示例:

java 复制代码
# HELP http_server_requests_seconds HTTP请求耗时
# TYPE http_server_requests_seconds histogram
http_server_requests_seconds_bucket{service="pay-service",status="200",endpoint="/pay",le="0.1"} 1245 1786923100000
http_server_requests_seconds_bucket{service="pay-service",status="500",endpoint="/pay",le="1"} 32 1786923100000
http_server_requests_seconds_sum{service="pay-service",status="200",endpoint="/pay"} 86.42 1786923100000
http_server_requests_seconds_count{service="pay-service",status="200",endpoint="/pay"} 1245 1786923100000

拆解对应结构:

  • namehttp_server_requests_seconds_bucket
  • labelsservice="pay-service",status="200",endpoint="/pay",le="0.1"
  • value1245
  • timestamp1786923100000
  • typehistogram(顶部注释元数据)

MicrometerOpenTelemetry 等开发/埋点 SDK 中,内部统一抽象指标对象数据结构,用于采集、计算、聚合,再转换成各种导出格式(PrometheusInfluxJaeger 等)。

指标对象 JSON 示例:

json 复制代码
{
  "name": "jvm_memory_used_bytes",
  "type": "gauge",
  "tags": [
    {"key": "area", "value": "heap"},
    {"key": "instance", "value": "127.0.0.1:8080"}
  ],
  "samples": [
    {
      "timestamp": 1786923100000,
      "value": 268435456
    }
  ]
}

内存结构化指标对象转换成 Prometheus 文本格式流程:

2. 指标名称(Metric Name)

2.1 核心概念

指标名称是一条监控时序数据唯一业务大类标识,用来描述「这是哪一类监控行为」,是区分不同业务监控数据的核心文本。

遵循 Micrometer 标准:全小写、小数点分层分隔

核心特点

  • 代表一类统一行为,粒度粗、语义固定;
  • 不携带动态、可变信息;
  • 跨监控后端自动转换格式(Prometheus/Graphite/Atlas);
  • 用来做大盘大类聚合、告警分组。

指标名示例

  • http.server.requests :服务端 HTTP 请求
  • db.mysql.queryMySQL 数据库查询
  • jvm.memory.usedJVM 内存占用
  • kafka.producer.sendKafka 生产者发送消息

2.2 指标命名规范

Micrometer 强制统一指标命名格式:

  • 全小写英文:指标名称全部由小写英文字母构成,不允许出现大写字母。
  • 使用小数点 . 分层分隔语义 :按照【领域.资源.动作】三层语义结构拆分指标含义,层级清晰、可读性强。
  • 不使用下划线、驼峰、横线 :严格禁止使用下划线 _、中横线 -、驼峰大小写作为分隔,仅能依靠小数点做分层。

✅ 规范标准指标名:

  • db.mysql.query
  • http.client.duration
  • jvm.memory.used

❌ 不符合规范写法:

  • db_mysql_query(下划线分隔)
  • DbMysqlQuery(驼峰大写)
  • db-mysql-query(横线分隔)

2.3 跨平台兼容

底层通过 NamingConvention 自动适配不同监控存储的命名语法,实现跨平台兼容。

标准指标名 http.server.requests 多平台自动转换示例:

  • Prometheus:不支持小数点,自动把 . 转为下划线,同时自动追加单位后缀 _seconds ,示例:http_server_requests_duration_seconds ;
  • AtlashttpServerRequests 驼峰命名风格,自动去除小数点并转为大驼峰,示例:httpServerRequests ;
  • Graphite:原生支持小数点分隔,直接复用原名称无转换,示例:http.server.requests ;
  • InfluxDB:原生兼容点分隔,原样输出,示例:http.server.requests

如果默认转换规则不满足业务需求,可自定义 NamingConvention 全局替换,所有 Meter 统一生效:

java 复制代码
// 全局自定义命名转换器
registry.config().namingConvention(new MyCustomNamingConvention());

3. 标签(Tag)

3.1 核心概念

标签Tag)是 Micrometer 指标体系中用于维度细分的键值对结构。

指标名用于定义「这是哪一类指标」,标签用于进一步拆分「该指标下不同场景、不同分支、不同资源的细分数据」。通过标签可以对同一指标做分组、筛选、聚合、告警拆分,是监控精细化、多维度统计的核心能力。

示例,定义统一指标名:http.server.requests(代表所有 HTTP 请求指标),搭配不同标签实现维度拆分:

  1. 标签 method=GET:单独统计 GET 请求的请求量、耗时、错误率;
  2. 标签 status=500:单独统计服务端异常请求数据;
  3. 标签 uri=/api/user/list:单独统计用户列表接口的监控数据。

如果没有标签,只能看到所有 HTTP 请求的笼统汇总数据,无法精准定位某个接口、某种请求方式的异常情况。

示例,通过不同标签拆分出多条独立时序数据:

java 复制代码
http.server.requests{method="GET",uri="/api/user/list",status="200"}  2560

http.server.requests{method="POST",uri="/api/order/create",status="200"}  1350

http.server.requests{method="POST",uri="/api/order/create",status="500"}  28

可以精准区分:哪个接口、什么请求方式、成功/失败的请求量,是监控告警、问题排查的核心依据。

3.2 标签编写规范

标签的使用有严格的业务分层规范,核心原则:指标名承载大类业务语义,标签仅用于细分维度,严禁本末倒置。

3.2.1 ✅ 推荐规范写法

指标名语义清晰、归属明确,代表一类统一行为;标签作为细分维度,用来区分不同资源、不同接口、不同场景,聚合结果具备业务意义。

java 复制代码
// 指标名:数据库调用(大类语义),标签细分具体数据库
registry.counter("database.calls", "db", "users");

// 指标名:HTTP请求(大类语义),标签细分具体接口路径
registry.counter("http.requests", "uri", "/api/users");

最终时序输出示例:

java 复制代码
# 数据库调用指标,维度清晰
database_calls_total{db="users"} 8920

# HTTP 请求指标,维度清晰
http_requests_total{uri="/api/users"} 12680

优势:全局聚合精准、维度清晰、大盘统计和告警规则简洁稳定。

3.2.2 ❌ 禁止反模式

禁止使用极度通用、无业务区分度的指标名,完全依靠标签区分业务场景。该写法会导致指标聚合混乱、数据混杂,完全失去统计价值。

java 复制代码
// 严重禁止!指标名过于笼统,数据库、HTTP、消息调用全部混杂在一起
// 聚合后的 calls 总量无任何业务意义,无法用于统计、告警、大盘展示
registry.counter("calls", "class", "database", "db", "users");

最终时序输出示例:

java 复制代码
calls_total{class="database",db="users"} 8920
calls_total{class="http",uri="/api/users"} 12680
calls_total{class="mq",topic="order"} 5600

问题根源:指标名 calls 没有业务归属,所有类型调用都会聚合到同一个指标下,数据污染严重,无法区分各类业务的真实吞吐量与错误率。

3.3 全局公共标签

3.3.1 核心概念

全局公共标签(Common Tags)是 MeterRegistry 注册表级别的统一标签,项目中所有指标会自动携带该批标签,无需每个埋点重复添加。

核心用途:统一区分环境、机房、集群、服务实例,实现多环境、多实例监控数据隔离,适配统一监控平台。

3.3.2 两种配置方式

方式一:可变参数快速配置

java 复制代码
registry.config().commonTags("stack", "prod", "region", "us-east-1");

方式二Tag 集合批量配置(可读性更强,适合多标签场景)

java 复制代码
registry.config().commonTags(Arrays.asList(
    Tag.of("stack", "prod"),
    Tag.of("region", "us-east-1")
));

3.3.3 最佳实践

Spring Boot 最佳实践:

  1. 固定静态公共标签(环境、服务名):优先使用 application.yml 配置文件统一声明,零代码侵入;

  2. 动态运行标签(实例IP、端口、集群节点):通过 MeterRegistryCustomizer 自定义 Bean 动态注入,适配容器化、云原生动态场景。

项目配置全局公共标签 stack=prodregion=us-east-1 后,所有指标会自动携带这两个维度,无需手动埋点添加,实现多环境数据隔离:

  • http.server.requests{method="GET",uri="/api/user/list",status="200",stack="prod",region="us-east-1"} 2560

  • database.calls{db="users",stack="prod",region="us-east-1"} 8960

  • http.requests{uri="/api/users",stack="prod",region="us-east-1"} 3210

优势:全量指标统一带上环境、机房标识,可快速筛选「生产环境」「指定机房」的全局监控数据,适配多集群统一监控大盘。

3.4 低基数标签

定义 :标签取值有限、固定、可枚举,不会无限新增,时序数量可控。

常见示例 :请求方式 GET/POST、响应状态码 200/400/500、环境 prod/test、接口模板路径、错误类型等。

核心价值:不会引发指标基数爆炸,监控时序数量稳定,适合聚合统计、可视化展示、阈值告警。

指标名 http.server.requests生成的时序为固定有限条数:

java 复制代码
http.server.requests{method="GET",status="200"}  1280
http.server.requests{method="POST",status="200"}  960
http.server.requests{method="GET",status="500"}  6

无论请求多少次,标签组合永远只有几十种,时序数量可控、稳定。

3.5 高基数标签

定义:标签取值无限增长、动态不重复、持续新增,无固定枚举范围。每产生一条新值,就会生成一条全新时序数据。

高基数标签会瞬间生成海量独立时序,引发指标基数爆炸 ,直接导致时序库内存溢出、磁盘爆满、查询超时、监控平台瘫痪。高基数唯一标识数据,统一存放于链路追踪Trace 中,严禁写入Metrics 指标标签。

强制避坑规范:

  1. 标签值禁止为null,空值统一收敛为固定枚举值;
  2. 禁止使用用户输入参数、动态 URI、用户 ID、订单 ID、流水号、TraceId 等高动态字段作为标签;
  3. 动态异常路径、未知接口,统一归类为NOT_FOUNDUNKNOWN 等固定值,杜绝无限新增标签。

错误使用 userId 动态字段打标签示例:

java 复制代码
http.server.requests{userId="10001"}  1
http.server.requests{userId="10002"}  1
http.server.requests{userId="10003"}  1

每来一个新用户,就新增一条全新时序。在线用户 10 万 = 瞬间新增 10 万条时序,直接压垮 Prometheus 监控服务。

高基数标签是指标埋点的高危禁区,必须严格规避,是线上监控雪崩的主要诱因之一。

4. 指标数值(Value)

定义:描述某一时刻监控对象业务状态的量化浮点数值,是 Metrics 承载业务信息的核心载体。

通用特性:

  1. 数据类型:标准为 double 64 位浮点数,支持整数、小数、NaN+Inf-Inf
  2. 语义绑定:数值含义由指标类型决定:
    • Gauge:瞬时快照值(内存、线程、CPU 占用,可升可降);
    • Counter:单调累计总量(请求数、异常数,只会递增);
    • Histogram:区分 bucket 区间计数、sum 总和、count 总次数三类数值;
  3. 两种存在形态:
    • 内存形态(Micrometer/OTel):动态计算函数,不缓存固定数值;
    • 传输文本形态(Prometheus 格式):静态固化数字,写入报文后不再变更。

5. 时间戳(Timestamp)

定义:标记指标数值采集瞬间的毫秒级 Unix 时间戳UTC 1970-01-01起算 13 位整数),是构建时序曲线的唯一核心维度。

通用特性:

  1. 时序主键规则:指标名称 + 标签集合 + timestamp 三者唯一确定一条时序数据点;
  2. 两大来源:应用端本地时钟生成、采集服务端拉取时刻时钟生成;
  3. 精度影响:时间戳粒度决定监控曲线的分辨能力,默认拉取模式下精度等于采集间隔。
相关推荐
2501_914245931 小时前
Java应用性能调优与生产监控部署:2026年实战手册
java·开发语言
吴声子夜歌1 小时前
MongoDB 4.x——微服务入门
java·mongodb·微服务
好好沉淀1 小时前
主键选择(自增 vs UUID vs 雪花算法)
java·算法
运维老郭1 小时前
Ubuntu下编译安装Redis:从0到1可用的完整指南
运维·云原生
带刺的坐椅2 小时前
Solon AI:Tool 与 Talent 怎么选?从函数到领域专家
java·ai·llm·agent·solon
小白说大模型2 小时前
AI Agent 调试实战:链路追踪、Prompt 可视化与异常定位的系统方法
java·人工智能·python·算法·prompt
静水楼台x2 小时前
spring security
java·数据库·spring
重生之我是Java开发战士2 小时前
【Java EE】Spring Boot配置文件
java·spring boot·java-ee
Tim_102 小时前
【C++】019、内存泄漏
java·开发语言