应用性能监测(APM)之 (五)Prometheus与OpenMetrics协议

1. OpenMetrics协议

1.1 OpenMetrics 是什么

OpenMetrics 是指标暴露文本协议规范 ,从 Prometheus 原始文本格式进化而来,目标是统一云原生指标的线上传输标准,脱离 Prometheus 本身,做到客户端、采集器无关。

  • 诞生:基于 Prometheus /metrics 文本格式,2020 发布 1.0 稳定版;

  • 现状:2024 年归档,合并回 Prometheus 项目,不再独立作为顶级 CNCF 项目,但协议本身继续演进(2.0 实验版);

  • 传输方式:服务暴露 HTTP /metrics 端点,Prometheus/其他采集器主动拉取(scrape)。Content-Type:application/openmetrics-text; version=1.0.0。

一句话定位:应用导出指标给监控系统的文本有线格式(wire format) 。 前面我们在 WAL 文档里看到的 Exemplar(追踪样例)、Native Histogram(原生直方图)、指标 # HELP / # TYPE 元数据,全部来自 OpenMetrics 规范定义。

1.2 OpenMetrics 支持的指标类型(和 WAL 记录一一对应)

  1. Counter:只增计数器(请求总数)

  2. Gauge:瞬时值(内存、CPU)

  3. Histogram:原生直方图(Native Histogram,对应 WAL type7/8/9/10)

  4. Summary:分位数摘要

  5. Info:静态元数据(版本、环境)

  6. Stateset:状态枚举(状态机:running/stopped)

  7. Untyped:未定义类型

1.3 OpenMetrics 文本样例(OM 1.0)

复制代码
 # HELP http_requests_total Total HTTP requests
 # TYPE http_requests_total counter
 # UNIT http_requests_total requests
 http_requests_total{method="GET",status="200"} 1234 # {trace_id="abc123",span_id="xyz789"} 1750000000000 10.5
 # EOF
  • # HELP:指标描述

  • # TYPE:指标类型

  • # UNIT:单位(OM新增,老Prom格式没有)

  • # {trace_id...}:Exemplar 样例,就是 WAL type=4 记录对应的来源,把追踪信息绑定到单个样本上。

  • # EOF:OM 规范强制末尾结束标记。

这就是 WAL 里 Exemplar、Metadata 的源头:应用按 OpenMetrics 输出文本 → Prometheus scrape 拉取 → 解析成时序、元数据、exemplar、直方图 → 写入 WAL(type1/2/4/6/7...)。

1.4 OpenMetrics 和 Prometheus WAL 的数据流关系

复制代码
 应用程序 → OpenMetrics文本(/metrics)
         ↓ scrape拉取
 Prometheus 采集器解析:
   1. 读到新的label集合 → WAL type=1 Series Record
   2. 读到普通样本 → WAL type=2 Samples Record
   3. 读到Exemplar追踪样例 → WAL type=4 Exemplar Record
   4. 读到#HELP/#UNIT/#TYPE → WAL type=6 Metadata Record
   5. 读到Native Histogram → WAL type7/8/9/10 Histogram Record
   6. 用户调用删除API → WAL type=3 Tombstones Record
         ↓
 写入WAL(先写WAL,再写入Head内存)
         ↓
 Head 内存(chunks_head)
         ↓ 达到块大小,切块
 Block(磁盘,包含index、chunks、tombstones文件)

1.5 OpenMetrics 和老 Prometheus 文本格式的核心差异

  1. 新增 # UNIT 单位元数据;

  2. 原生支持 Exemplar(追踪样例);

  3. 原生支持 Native Histogram 原生直方图;

  4. 强制 # EOF 结束标记;

  5. 规范更严谨,想推动成为 IETF 标准;

  6. 支持类型更丰富:Info、Stateset。

1.6 常见误区

  1. ❌ OpenMetrics ≠ Prometheus。OpenMetrics只是指标导出文本协议;Prometheus是完整时序数据库,包含WAL、存储、查询、采集。

  2. ❌ OpenMetrics 和 OpenTelemetry Metrics 不是同一个东西:

    • OpenMetrics:文本拉取协议,用于应用暴露指标;

    • OpenTelemetry Metrics:遥测SDK,可以推指标,支持多种编码(protobuf/json),也可以导出为OpenMetrics。

  3. ❌ OpenMetrics 2024归档=废弃:归档只是合并回Prometheus仓库,协议继续迭代,大量exporter仍在输出OpenMetrics。

2. OpenMetrics 7种指标类型示例

全部遵循 OpenMetrics 1.0 文本格式,包含 # HELP / # TYPE,部分带 # UNIT。

2.1. Counter 只增计数器

含义:单调递增,只往上加,不会下降;只有进程重启才会归零。适合请求总数、错误总数。

复制代码
 # HELP http_requests_total Total HTTP requests received
 # TYPE http_requests_total counter
 # UNIT http_requests_total requests
 http_requests_total{method="GET",status="200"} 15689
 http_requests_total{method="POST",status="500"} 247
 # EOF

2. Gauge 仪表盘/瞬时值

含义:可增可减,代表某一瞬间的快照。内存、连接数、CPU使用率、队列长度。

复制代码
 # HELP process_memory_usage_bytes Current memory used by the process
 # TYPE process_memory_usage_bytes gauge
 # UNIT process_memory_usage_bytes bytes
 process_memory_usage_bytes{pid="1234"} 23560192
 # EOF

3. Histogram

传统桶式直方图

复制代码
 # HELP http_request_duration_seconds HTTP request latency
 # TYPE http_request_duration_seconds histogram
 http_request_duration_seconds_bucket{le="0.01"} 120     # ≤ 0.01s
 http_request_duration_seconds_bucket{le="0.05"} 890     # ≤ 0.05s
 http_request_duration_seconds_bucket{le="0.1"} 3200     # ≤ 0.1s
 http_request_duration_seconds_bucket{le="0.5"} 4500    # ≤ 0.5s
 http_request_duration_seconds_bucket{le="+Inf"} 4560   # ≤ +∞
 http_request_duration_seconds_sum 125.4
 http_request_duration_seconds_count 4560
 # EOF

原生直方图 Native Histogram

注意:这是Native Histogram(原生直方图),不是传统的 bucket 拆分直方图;一条时序内部自带分布桶,对应 WAL type7/8/9/10。

复制代码
 # HELP http_request_duration_seconds Request latency distribution
 # TYPE http_request_duration_seconds histogram
 # UNIT http_request_duration_seconds seconds
 http_request_duration_seconds{method="GET"} {schema="3",zero_threshold="0.001"} 128 0.45 10 20 30
 # EOF

传统分桶直方图(老版本,不是Native Histogram)是拆成多个bucket时序,OM同样支持,但不属于Native Histogram。

4. Summary 摘要(分位数)

含义:预先在客户端计算好分位数(p50/p90/p99),直接上报分位结果。

缺点:分位数在SDK侧预先算死,服务端不能换分位数查询。

复制代码
 # HELP api_response_latency_seconds Summary of API latency
 # TYPE api_response_latency_seconds summary
 # UNIT api_response_latency_seconds seconds
 api_response_latency_seconds{quantile="0.5"} 0.023
 api_response_latency_seconds{quantile="0.9"} 0.081
 api_response_latency_seconds{quantile="0.99"} 0.215
 api_response_latency_seconds_sum 1254.32
 api_response_latency_seconds_count 48200
 # EOF

5. Info 静态元数据指标

含义:一次性静态信息,版本号、环境、构建信息,值固定为1,标签携带信息。

复制代码
 # HELP app_info Application metadata
 # TYPE app_info info
 app_info{version="v2.1.0",env="prod",git_commit="aef123"} 1
 # EOF

6. Stateset 状态集合(状态枚举)

含义 :一组互斥状态,同一时间只能有一个状态=1,其余=0,描述状态机。例如服务运行状态。

复制代码
 # HELP service_state Service running state
 # TYPE service_state stateset
 service_state{state="running"} 1
 service_state{state="stopped"} 0
 service_state{state="error"} 0
 # EOF

7. Untyped 未定义类型

含义:不知道指标类型,兼容旧数据;不推荐新指标使用。

复制代码
 # HELP legacy_metric A legacy metric without defined type
 # TYPE legacy_metric untyped
 legacy_metric{name="old_job"} 88.5
 # EOF

3. 传统分桶直方图(Classic Bucket Histogram) vs Native Histogram(原生直方图)

传统直方图:Prometheus 早期方案,也是很多exporter默认输出的; Native Histogram:OpenMetrics 1.0引入,Prometheus 2.40+正式支持,对应WAL type7/8/9/10。

3.1. 文本格式对比(OpenMetrics)

传统 Bucket Histogram(多时序方案)

同一个逻辑直方图,拆成多条独立Counter时序

复制代码
 # HELP http_request_duration_seconds HTTP request latency
 # TYPE http_request_duration_seconds histogram
 http_request_duration_seconds_bucket{le="0.01"} 120     # ≤ 0.01s
 http_request_duration_seconds_bucket{le="0.05"} 890     # ≤ 0.05s
 http_request_duration_seconds_bucket{le="0.1"} 3200     # ≤ 0.1s
 http_request_duration_seconds_bucket{le="0.5"} 4500    # ≤ 0.5s
 http_request_duration_seconds_bucket{le="+Inf"} 4560   # ≤ +∞
 http_request_duration_seconds_sum 125.4
 http_request_duration_seconds_count 4560
 # EOF
  • le:bucket上限标签;

  • 每一个桶都是独立时序;_sum、_count 额外两条;

  • 上面这个例子:5个桶 → 一共 7条时序。

Native Histogram(原生直方图,单时序承载全部分布)

复制代码
 # HELP http_request_duration_seconds HTTP request latency
 # TYPE http_request_duration_seconds histogram
 # UNIT http_request_duration_seconds seconds
 http_request_duration_seconds{method="GET"} {schema="3",zero_threshold="0.001"} 4560 125.4 120 770 2310 1300 60
 # EOF
  • 仅1条时序 ;桶、count、sum、零桶信息全部编码在单样本内部;

  • 不再产生一堆带le标签的子时序。

复制代码
 区间内的**增量计数**:
 1. [0, 0.01] → 120
 2. (0.01, 0.05] → 770
 3. (0.05, 0.1] → 2310
 4. (0.1, 0.5] →1300
 5. (0.5, ∞) →60

语法结构: metric_name{labels} {options} count sum bucket1 bucket2 bucket3 ...

分段拆解

  1. http_request_duration_seconds{method="GET"} 时序名称 + 标签。代表:GET 请求的延迟指标,只有这1条时序 (传统直方图会拆成一堆带le的子时序)。

  2. {schema="3",zero_threshold="0.001"} 直方图配置参数

  • schema="3":指数桶的分辨率。 schema 取值范围 -4 ~ 8,数字越大,桶越精细。 schema=3:每一个桶的上限 = 前一个桶 × 2^(1/2^3) = 2^(1/8) ≈ 1.0905。 → 每下一个桶比上一个桶大9.05%。 schema越大,精度越高,但桶数量越多,存储开销上升。

  • zero_threshold="0.001":零桶阈值 0.001秒(1ms) 落在区间 [-0.001, +0.001] 的样本,全部归入零桶,不再进入正负指数桶。 目的:把极靠近0的微小噪声收拢到一个桶,避免产生大量极小值空桶,减少存储。

  1. 4560 → count(总观测样本数) 一共采集到 4560次GET请求。

  2. 125.4 → sum(所有样本总和,单位秒) 全部4560次请求耗时加起来 = 125.4秒。

    平均延迟 = sum / count = 125.4 / 4560 ≈ 0.0275s = 27.5ms

  3. 后面一串数字:120 770 2310 1300 60 这是各个桶里面的请求计数(正数侧桶,本次例子没有负数样本)。

    这里简化写法,省略了完整的spans描述,实际wire格式/WAL里是用span描述:哪些桶索引有值,空桶不保存,实现稀疏压缩。

    • 120:落在第一个有效桶的请求数量

    • 770:第二个桶

    • 2310:第三个桶(最多的请求落在这个延迟区间)

    • 1300:第四个桶

    • 60:第五个桶

校验总和:120+770+2310+1300+60 = 4560,刚好等于count,说明零桶里面样本数量=0,没有请求落在 -1ms,1ms 区间。

语义一句话总结

GET请求延迟,使用schema=3指数桶,1ms以内的请求归入零桶;一共4560次请求,总耗时125.4秒;所有请求分布在5个正数侧指数桶,没有请求落在±1ms以内。 Prometheus收到这条样本后,写入WAL type7 整数原生直方图记录。

和传统Bucket直方图对比(直观感受)

传统直方图要写成多条时序:

复制代码
 http_request_duration_seconds_bucket{le="0.01"} 120
 http_request_duration_seconds_bucket{le="0.05"} 890
 http_request_duration_seconds_bucket{le="0.1"} 3200
 http_request_duration_seconds_bucket{le="0.5"} 4500
 http_request_duration_seconds_bucket{le="+Inf"} 4560
 http_request_duration_seconds_sum 125.4
 http_request_duration_seconds_count 4560
  • 传统:多条独立时序

  • Native Histogram:单条时序,样本内部打包全部分布信息

对应的PromQL查询示例

复制代码
 # p99延迟
 histogram_quantile(0.99, http_request_duration_seconds)
 # p50中位数
 histogram_quantile(0.5, http_request_duration_seconds)
 # 5m速率,再算p99(推荐生产写法)
 histogram_quantile(0.99, rate(http_request_duration_seconds[5m]))

重要细节

  1. 桶边界不是固定写死的数字 ,是由 schema 公式动态计算 ,不是像传统直方图那样写死le="0.1"。

  2. 空桶不存储,用span压缩,所以不会保存全部桶,只保存有计数的桶。

  3. 零桶是独立的,专门收拢靠近0的噪声。

如果你想,我可以把这条原生直方图转成WAL type7的二进制结构伪代码,对照前面wal.md的格式。

3.2. 核心差异总表

对比项 传统Bucket Histogram Native Histogram
时序数量 N个桶 → N+2条时序(bucket + sum + count),基数爆炸 1条时序承载完整分布,极大降低cardinality
分位数 查询时服务端基于le桶插值计算;桶边界写死在exporter侧 服务端任意分位数(p50/p90/p99.9/p99.99),不用预定义桶
桶边界 固定线性/自定义桶,修改桶边界等于创建全新时序 指数桶(默认),也支持NHCB自定义桶;桶边界可动态调整,不新增时序
聚合能力 多实例聚合麻烦,只能聚合到固定桶边界;跨实例分位数失真 支持sum聚合分布,合并多个直方图,聚合后依然可以算任意分位数
存储 每个bucket独立chunk,存储开销大,高基数场景压力巨大 内置稀疏桶Span压缩,空桶不存储,存储效率更高
WAL记录 普通type=2 Samples Record,多条时序分别写入 专用type7/8/9/10 Histogram记录,单条记录保存完整分布
查询开销 计算分位数需要遍历所有bucket时序,查询复杂 直接操作分布结构,分位数计算更快;但直方图合并逻辑更重
兼容性 所有版本Prometheus都支持 需要Prometheus ≥2.40,老版本不识别
精度 桶越密精度越高,但桶越多时序爆炸;桶稀疏则分位数误差大 指数桶,小值区域桶细、大值区域桶变粗;可通过schema调整分辨率

3.3. 优缺点拆解

传统 Bucket Histogram

✅ 优点

  1. 兼容性极好,生态成熟,所有exporter都支持;

  2. 逻辑简单,底层就是普通counter;

  3. 可以直接对固定桶做告警(le="0.1")。

❌ 缺点

  1. 基数爆炸(最大痛点):一个实例+10个桶就是12条时序;几百实例直接几万时序;

  2. 桶边界写死在采集侧,想增加/修改桶边界,会生成新时序;

  3. 分位数只能在预先定义的桶之间插值;

  4. 多实例聚合后,分位数估算偏差很大。

Native Histogram

✅ 优点

  1. 大幅降低时序基数,解决高基数噩梦;

  2. 服务端任意分位数查询,不需要预先定义桶;

  3. 天然支持分布聚合:多个实例直方图sum合并后,依然可以计算p99/p99.9;

  4. 稀疏桶压缩,空桶不占用存储;

  5. 桶分辨率schema可调整,不需要改动标签、不产生新时序。

❌ 缺点

  1. 逻辑复杂:采集、WAL、chunk、查询、compaction全链路都有特殊处理;

  2. 旧版本Prometheus不识别;

  3. 无法直接基于固定桶边界写告警(桶边界不是固定离散标签);

  4. 直方图合并、计算会有少量精度损失;

  5. 客户端SDK支持还在普及阶段。

3.4. 生产选型建议

  1. 新业务、延迟类指标:优先 Native Histogram;解决基数爆炸,灵活查询任意分位数;

  2. 老系统、需要基于固定桶告警:继续传统bucket直方图;

  3. 不要混用:同一个指标不要同时输出传统bucket和native histogram。

4. Grafana、Mimir、VictoriaMetrics(VM)Native Histogram 支持版本

新格式 = Prometheus Native Histogram(指数桶+NHCB自定义分桶,对应 WAL type7/8/9/10) 传统Bucket直方图所有版本全部支持,下面只讲原生直方图。

1. Grafana(前端可视化)

Grafana 从 v9.4 开始实验性支持原生直方图; v10.0+ 正式稳定支持,可以直接渲染原生直方图的热图、分位数面板。

  • v10.x:支持 histogram_quantile()、原生直方图heatmap;

  • v11+:进一步完善NHCB自定义分桶直方图可视化。

注意:Grafana只是展示层,底层TSDB(Mimir/Prometheus/VM)必须支持原生直方图查询,Grafana才能拿到数据。

2. Grafana Mimir

版本 状态 说明
2.7 实验 仅支持写入Ingest,不能查询 ,需要开启flag ingester.native-histograms-ingestion-enabled
2.8+ beta 支持写入+查询指数桶Native Histogram;NHCB自定义桶后续迭代完善
2.16 稳定,默认开启 Ingester默认开启原生直方图,支持乱序原生直方图,生产可用(推荐最低基线)
2.17+ 完善 支持OTel直方图转NHCB自定义分桶直方图,MQE引擎深度优化直方图聚合
3.x 成熟 MQE默认引擎,NHCB完整支持,支持直方图trim算子等高级PromQL

生产建议:Mimir ≥ 2.16 再上Native Histogram。

3. VictoriaMetrics(VM,单节点/集群)

⚠️ VM和Mimir/Prometheus实现逻辑不一样:收到Prometheus原生直方图后,自动内部转换成VM自己的直方图格式,对外暴露为_count/_sum/_bucket时序,带vmrange标签 ,可以直接用histogram_quantile()查询。

  • v1.143.0 :首次引入原生直方图支持,vmagent、单节点、集群都可以摄入Prometheus原生直方图(指数桶)

  • v1.153.0:完善NHCB自定义分桶原生直方图支持,remote read模式支持迁移原生直方图,保留自定义桶边界

重要限制:VM 不原生保留Prometheus原生直方图的内部结构,写入即转成VM直方图模型; Remote Write 2.0协议VM目前不支持。

4. Prometheus 本身(顺带补齐,前面WAL的源头)

  • v2.40:正式GA支持Native Histogram(指数桶),WAL type7/8

  • v2.45:增加NHCB自定义分桶直方图 type9/10

  • v3.0:完善Remote Write v2.0,支持原生直方图远程转发给Mimir

5. 生产部署完整链路版本底线(端到端)

采集端(Prometheus/Alloy)→ RemoteWrite → Mimir/VM → Grafana看板

方案A:Mimir(原生保存Native Histogram,推荐)

  • Prometheus ≥2.40 或 Grafana Alloy ≥1.11

  • Mimir ≥2.16

  • Grafana ≥10.0

方案B:VictoriaMetrics(自动转换)

  • Prometheus ≥2.40

  • VictoriaMetrics ≥1.143.0

  • Grafana ≥10.0

关键坑点

  1. Mimir 2.7~2.15:虽然能写入,但查询、聚合、NHCB不稳定,不建议生产;

  2. VM:原生直方图进入VM后会被转成vmrange桶,不再是Prometheus原生直方图结构;跨系统迁移要留意;

  3. 远程写入协议:传递Native Histogram必须使用 Remote Write v2(protobuf),老的v1文本remote write不支持;

  4. 老Grafana <10:能查分位数,但无法渲染原生直方图热图。

相关推荐
Cheney Pan3 小时前
第13篇 监控安全与权限治理
安全·prometheus
蓝胖的四次元口袋2 天前
Prometheus+Grafana知识梳理(2)
grafana·prometheus
Cheney Pan3 天前
第8篇 关键指标分析方法:RED/USE 与黄金信号
prometheus
Cheney Pan4 天前
第7篇 业务数据监控:SQL Exporter 与自定义业务指标
prometheus·exporter
Cheney Pan4 天前
第4篇 Spring Boot 应用监控:Micrometer 与 JVM 指标
jvm·spring boot·后端·prometheus
飞鸟真人4 天前
应用性能监测(APM)之 (四)Prometheus与Metrics集群存储
prometheus
Cheney Pan4 天前
第09篇 告警体系设计:Alertmanager 路由与告警治理
prometheus
蓝胖的四次元口袋5 天前
Prometheus+Grafana知识梳理(1)
grafana·prometheus
晨陌y5 天前
CentOS 7 部署 mysqld_exporter:MySQL 指标采集、Prometheus 告警与远程监控
mysql·centos·prometheus