Prometheus 远程写入 2.0 规范 [实验性]

  • 版本: 2.0-rc.3
  • 状态: 实验性
  • 日期: 2024 年 5 月

远程写入规范旨在普遍记录 Prometheus 以及与 Prometheus 远程写入兼容的发送方如何将数据发送到 Prometheus 或与 Prometheus 远程写入兼容的接收方的标准。

本文档旨在定义 Prometheus 远程写入 API 的第二个版本,其中对协议和语义进行了微小更改。该第二个版本引入了一个新的 Protobuf 消息,其新功能可在性能和成本节约的基础上支持更多用例和更广泛的采用。第二个版本还废弃了 1.0 远程写入规范中的先前 Protobuf 消息,并添加了强制性的 X-Prometheus-Remote-Write-*-Written HTTP 响应头,以提高可靠性。最后,本规范概述了如何使用现有的基本内容协商请求头实现向后兼容的发送方和接收方(即使在单个端点下)。如果需要,更高级的自动内容协商机制可能会在未来的次要版本中推出。有关 2.0 规范背后的原理,请参阅正式提案

本文档中的关键词“必须(MUST)”、“不得(MUST NOT)”、“要求(REQUIRED)”、“应(SHALL)”、“不应(SHALL NOT)”、“建议(SHOULD)”、“不建议(SHOULD NOT)”、“推荐(RECOMMENDED)”、“可以(MAY)”和“可选(OPTIONAL)”应按照 RFC 2119 中的描述进行解释。

注意这是远程写入 2.0 规范的发布候选版本。这意味着本规范目前处于实验性状态——预计不会有重大更改,但我们保留根据早期采用者的反馈,在必要时破坏兼容性的权利。潜在的反馈、问题和建议应作为评论添加到开放提案的 PR中。

简介

背景

远程写入协议旨在使样本能够实时、可靠地从发送方传播到接收方,而不会丢失。

远程写入协议被设计为无状态的;严格来说,没有消息间的通信。因此,该协议不被认为是“流式传输”。为了实现流式效果,应使用例如 HTTP/1.1 或 HTTP/2 通过同一连接发送多条消息。曾考虑过 gRPC 等“高级”技术,但当时这些技术尚未广泛采用,并且将 gRPC 服务暴露在 AWS EC2 ELB 等负载均衡器后的互联网上具有挑战性。

远程写入协议包含批处理的机会,例如,在单个请求中为不同的序列发送多个样本。虽然 Protobuf 消息中支持这一点,但预计同一序列的多个样本不会普遍在同一请求中发送。

测试套件可在 https://github.com/prometheus/compliance/tree/main/remote_write_sender 找到。远程写入 2.0 兼容性的合规性测试仍在进行中

术语表

本文档遵循以下定义

  • Remote-Write 是此 Prometheus 协议的名称。
  • 协议(Protocol)是一种通信规范,使客户端和服务器能够传输指标。
  • Protobuf 消息(或 Proto 消息)是指本协议数据结构中内容类型的定义。由于该规范专门使用 Google Protocol Buffers ("protobuf"),因此模式定义在 “.proto”文件中,并由单个 Protobuf “消息”表示。
  • 传输格式(Wire Format)是数据在网络中传输时的格式。对于远程写入,这始终是压缩的二进制 protobuf 格式。
  • 发送方(Sender)是发送远程写入数据的实体。
  • 接收方(Receiver)是接收(写入)远程写入数据的实体。“已写入”(Written)的含义由接收方决定,例如,通常它意味着将接收到的数据存储在数据库中,但也可能仅仅是验证、拆分或增强数据。
  • Written 指的是 Receiver 已接收并接受的数据。无论是否已将此数据摄取到持久存储、写入 WAL 等,都由 Receiver 决定。唯一的区别是 Receiver 已接受此数据,而不是以错误响应明确拒绝。
  • 样本(Sample)是一对(时间戳,值)。
  • 直方图(Histogram)是一对(时间戳,直方图值)。
  • 标签(Label)是一对(键,值)。
  • 序列(Series)是样本的列表,由一组唯一的标签标识。

定义

协议

远程写入协议必须(MUST)由 RPC 组成,其请求正文使用 Google Protocol Buffers 序列化,然后进行压缩。

protobuf 序列化必须(MUST)使用以下任一 Protobuf 消息

  • 远程写入 1.0 规范中引入的 prometheus.WriteRequest。截至 2.0 版本,此消息已被弃用。它应(SHOULD)仅用于兼容性目的。发送方和接收方可以(MAY)不(NOT)支持 prometheus.WriteRequest
  • 本规范中引入并定义在下文io.prometheus.write.v2.Request。发送方和接收方应(SHOULD)在可能的情况下使用此消息。发送方和接收方必须(MUST)支持 io.prometheus.write.v2.Request

Protobuf 消息必须(MUST)使用二进制传输格式。然后,必须(MUST)使用 Google 的 Snappy 进行压缩。Snappy 的块格式必须(MUST)使用——帧格式不得(MUST NOT)使用。

发送方必须(MUST)在 HTTP POST 请求的正文中发送序列化并压缩的 Protobuf 消息,并通过 HTTP 将其发送到接收方提供的 URL 路径。接收方可以(MAY)指定任何 HTTP URL 路径来接收指标。

发送方必须(MUST)随 HTTP 请求发送以下保留头

  • Content-Encoding
  • Content-Type
  • X-Prometheus-Remote-Write-Version
  • User-Agent

发送方可以(MAY)允许用户添加自定义 HTTP 头;但不得(MUST NOT)允许用户以发送保留头的方式配置它们。

Content-Encoding

Content-Encoding: <compression>

内容编码请求头必须(MUST)遵循 RFC 9110。发送方必须(MUST)使用 snappy 值。接收方必须(MUST)支持 snappy 压缩。新的可选压缩算法可能会在 2.x 或更高版本中推出。

Content-Type

Content-Type: application/x-protobuf
Content-Type: application/x-protobuf;proto=<fully qualified name>

内容类型请求头必须(MUST)遵循 RFC 9110。发送方必须(MUST)仅使用 application/x-protobuf 作为媒体类型。发送方可以(MAY)在头部值中添加 ;proto= 参数,以指示所使用的 Protobuf 消息的完全限定名称(即上述两种消息之一)。因此,发送方必须(MUST)发送以下三种受支持的头部值中的任意一种

对于 PRW 1.0 中引入的已弃用消息,标识符为 prometheus.WriteRequest

  • Content-Type: application/x-protobuf
  • Content-Type: application/x-protobuf;proto=prometheus.WriteRequest

对于 PRW 2.0 中引入的消息,标识符为 io.prometheus.write.v2.Request

  • Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request

当与 1.x 接收方通信时,发送方应(SHOULD)使用 Content-Type: application/x-protobuf 以实现向后兼容。否则,发送方应(SHOULD)使用 Content-Type: application/x-protobuf;proto=io.prometheus.write.v2.Request。更多 Protobuf 消息可能会在 2.x 或更高版本中推出。

接收方必须(MUST)使用内容类型头来识别要使用的 Protobuf 消息模式。意外选择错误的模式可能导致非确定性行为(例如数据损坏)。

注意由于 io.prometheus.write.v2.Request 中的保留字段,接收方意外地将错误模式与 prometheus.WriteRequest 一起使用将导致空消息。这通常是为了方便避免意外错误,但不要依赖它——未来的 Protobuf 消息可能不具备此功能。

X-Prometheus-Remote-Write-Version

X-Prometheus-Remote-Write-Version: <Remote-Write spec major and minor version>

当与 1.x 接收方通信时,发送方必须(MUST)使用 X-Prometheus-Remote-Write-Version: 0.1.0 以实现向后兼容。否则,发送方应(SHOULD)使用与其兼容的最新远程写入版本,例如 X-Prometheus-Remote-Write-Version: 2.0.0

User-Agent

User-Agent: <name & version of the Sender>

发送方必须(MUST)包含一个用户代理头,该头应(SHOULD)遵循 RFC 9110 用户代理头格式

响应

成功写入所有数据的接收方必须(MUST)返回一个 2xx 成功 HTTP 状态码。在这种成功情况下,接收方的响应正文应(SHOULD)为空,状态码应(SHOULD)为 204 HTTP 无内容(No Content);发送方必须(MUST)忽略响应正文。响应正文保留(RESERVED)供将来使用。

如果接收方已知发送数据的任何部分(例如样本、直方图、Exemplars)未成功写入(无论是部分写入还是全部写入拒绝),则接收方不得(MUST NOT)返回 2xx HTTP 状态码。在这种情况下,接收方必须(MUST)在响应正文中提供人类可读的错误消息。接收方的错误应(SHOULD)包含有关被拒绝样本数量和原因的信息。发送方不得(MUST NOT)尝试解释错误消息,并应(SHOULD)按原样记录。

以下小节详细说明了发送方和接收方在头信息和不同写入错误情况下的语义。

必需的“已写入”响应头

内容协商成功后,接收方会处理(写入)接收到的数据批次。对于每项重要数据(目前包括样本、直方图和 Exemplars),一旦处理完成(无论成功或失败),接收方必须(MUST)发送一个专门的 HTTP X-Prometheus-Remote-Write-*-Written 响应头,其中包含成功写入元素的精确数量。

每个头部值必须(MUST)是单个 64 位整数。头部名称必须(MUST)如下

X-Prometheus-Remote-Write-Samples-Written <count of all successfully written Samples>
X-Prometheus-Remote-Write-Histograms-Written <count of all successfully written Histogram samples>
X-Prometheus-Remote-Write-Exemplars-Written <count of all successfully written Exemplars>

在收到 2xx 或 4xx 状态码时,发送方可以(CAN)假设任何缺失的 X-Prometheus-Remote-Write-*-Written 响应头意味着接收方没有写入该类别(例如样本)的任何元素(计数为 0)。然而,当使用已弃用的 prometheus.WriteRequest Protobuf 消息时,发送方不得(MUST NOT)作此假设,因为存在遇到不支持此功能的 1.0 接收方的风险。

发送方可以(MAY)使用这些头部来确认接收方成功写入了哪些数据部分。常见用例包括

  • 更好地处理部分写入失败情况:发送方可以(MAY)使用这些头部进行更精确的客户端埋点和错误处理。
  • 检测损坏的 1.0 接收方实现:当使用 io.prometheus.write.v2.Request 请求发送数据并接收到 2xx HTTP 状态码,但未从接收方收到任何 X-Prometheus-Remote-Write-*-Written 响应头时,发送方应(SHOULD)假定为 415 HTTP 不支持的媒体类型状态码。这是 1.0 接收方常见的问题,它们不检查 Content-Type 请求头;意外地使用 prometheus.WriteRequest 模式解码 io.prometheus.write.v2.Request 有效负载会导致空结果且没有解码错误。
  • 检测其他损坏的实现或问题:发送方可以(MAY)使用这些头部来检测损坏的发送方和接收方实现或其他问题。

发送方不得(MUST NOT)从远程写入响应头中猜测接收方实现了哪个远程写入规范版本。

未来可能会有更多(可选)头部,例如,当添加更多实体或字段且需要确认时。

部分写入

发送方应(SHOULD)使用远程写入在单个请求中发送多个序列的样本。因此,接收方可以(MAY)在包含一些无效或未写入样本的写入请求中写入有效样本,这表示部分写入情况。在这种情况下,接收方必须(MUST)根据“无效样本”和“部分写入重试”部分返回非 2xx 状态码。

不支持的请求内容

如果接收方不支持发送方提供的给定内容类型或编码,则接收方必须(MUST)返回 415 HTTP 不支持的媒体类型状态码。

出于向后兼容性考虑,发送方应(SHOULD)预期 1.x 接收方因上述原因返回 400 HTTP 错误请求

无效样本

接收方可以(MAY)不(NOT)支持某些指标类型或样本(例如,一个接收方可能拒绝未指定元数据类型或未创建时间戳的样本,而另一个接收方可能接受此类样本)。哪个样本无效由接收方决定。除非发生可部分重试的写入,否则接收方必须(MUST)对包含任何无效样本的写入请求返回 400 HTTP 错误请求状态码。

发送方不得(MUST NOT)对 4xx HTTP 状态码(429 除外)进行重试,这些状态码必须(MUST)由接收方用于指示写入操作永远无法成功且不应重试。发送方可以(MAY)对 415 HTTP 状态码进行重试,尝试使用不同的内容类型或编码,以查看接收方是否支持。

重试与指数退避

接收方可以(MAY)返回 429 HTTP 请求过多状态码以指示服务器过载情况。接收方可以(MAY)返回 Retry-After 头以指示下一次写入尝试的时间。接收方可以(MAY)返回 5xx HTTP 状态码以表示内部服务器错误。

发送方可以(MAY)对 429 HTTP 状态码进行重试。发送方必须(MUST)对 5xx HTTP 写入请求进行重试。发送方必须(MUST)使用指数退避算法以防止服务器过载。发送方可以(MAY)处理 Retry-After 响应头以估计下一次重试时间。

429 与 5xx 处理之间的区别在于,当接收方无法跟上请求量,或者接收方选择对发送方进行速率限制以保护其可用性时,发送方可能“滞后”。因此,发送方可以选择不(NOT)对 429 进行重试,这允许在发送方出现错误(例如流量过大)时取得进展,同时在接收方出现错误(5xx)时数据不会丢失。

部分写入重试

在部分写入或部分无效样本情况下,当接收方期望发送方重试整个请求时,接收方可以(MAY)返回 5xx HTTP 或 429 HTTP 状态码。在这种情况下,接收方必须(MUST)支持幂等性,因为发送方可以(MAY)使用相同的请求进行重试。

向后和向前兼容性

该协议遵循 语义化版本控制 2.0:任何 2.x 兼容的接收器(Receiver)必须能够读取任何 2.x 兼容的发送器(Sender),反之亦然。破坏性或向后不兼容的更改将导致规范版本变为 3.x。

Protobuf 消息(线格式)本身在某些方面是向前/向后兼容的

  • 从 Protobuf 消息中删除字段需要提升主版本号。
  • 添加(可选)字段可以通过提升次版本号来完成。

换句话说,这意味着未来的 2.x 次要版本可以为 io.prometheus.write.v2.Request 添加新的可选字段、新的压缩方式、Protobuf 消息和协商机制,只要它们是向后兼容的(例如,对接收器和发送器都是可选的)。

2.x 与 1.x 兼容性

2.x 协议通过引入一个新的强制性 io.prometheus.write.v2.Request Protobuf 消息并废弃 prometheus.WriteRequest 来打破与 1.x 的兼容性。

2.x 发送器可以通过允许用户配置发送器应使用哪种内容类型来支持 1.x 接收器。如果接收器返回 415 HTTP 状态码,2.x 发送器也可以自动回退到不同的内容类型。

Protobuf 消息

io.prometheus.write.v2.Request

io.prometheus.write.v2.Request 引用了新的 Protobuf 消息,旨在替换和废弃 Remote-Write 1.0 的 prometheus.WriteRequest 消息。

完整的 schema 和真实来源位于 Prometheus 仓库的 prompb/io/prometheus/write/v2/types.proto 中。gogo 依赖项和选项可以忽略(最终将被移除)。它们不属于规范的一部分,因为它们不影响序列化格式。

新的 io.prometheus.write.v2.Request 的简化版本如下所示。

// Request represents a request to write the given timeseries to a remote destination.
message Request {
  // Since Request supersedes 1.0 spec's prometheus.WriteRequest, we reserve the top-down message
  // for the deterministic interop between those two.
  // Generally it's not needed, because Receivers must use the Content-Type header, but we want to
  // be sympathetic to adopters with mistaken implementations and have deterministic error (empty
  // message if you use the wrong proto schema).
  reserved 1 to 3;

  // symbols contains a de-duplicated array of string elements used for various
  // items in a Request message, like labels and metadata items. For the sender's convenience
  // around empty values for optional fields like unit_ref, symbols array MUST start with
  // empty string.
  //
  // To decode each of the symbolized strings, referenced, by "ref(s)" suffix, you
  // need to lookup the actual string by index from symbols array. The order of
  // strings is up to the sender. The receiver should not assume any particular encoding.
  repeated string symbols = 4;
  // timeseries represents an array of distinct series with 0 or more samples.
  repeated TimeSeries timeseries = 5;
}

// TimeSeries represents a single series.
message TimeSeries {
  // labels_refs is a list of label name-value pair references, encoded
  // as indices to the Request.symbols array. This list's length is always
  // a multiple of two, and the underlying labels should be sorted lexicographically.
  //
  // Note that there might be multiple TimeSeries objects in the same
  // Requests with the same labels e.g. for different exemplars, metadata
  // or created timestamp.
  repeated uint32 labels_refs = 1;

  // Timeseries messages can either specify samples or (native) histogram samples
  // (histogram field), but not both. For a typical sender (real-time metric
  // streaming), in healthy cases, there will be only one sample or histogram.
  //
  // Samples and histograms are sorted by timestamp (older first).
  repeated Sample samples = 2;
  repeated Histogram histograms = 3;

  // exemplars represents an optional set of exemplars attached to this series' samples.
  repeated Exemplar exemplars = 4;

  // metadata represents the metadata associated with the given series' samples.
  Metadata metadata = 5;

  // created_timestamp represents an optional created timestamp associated with
  // this series' samples in ms format, typically for counter or histogram type
  // metrics. Created timestamp represents the time when the counter started
  // counting (sometimes referred to as start timestamp), which can increase
  // the accuracy of query results.
  //
  // Note that some receivers might require this and in return fail to
  // write such samples within the Request.
  //
  // For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
  // for conversion from/to time.Time to Prometheus timestamp.
  //
  // Note that the "optional" keyword is omitted due to
  // https://cloud.google.com/apis/design/design_patterns.md#optional_primitive_fields
  // Zero value means value not set. If you need to use exactly zero value for
  // the timestamp, use 1 millisecond before or after.
  int64 created_timestamp = 6;
}

// Exemplar represents additional information attached to some series' samples.
message Exemplar {
  // labels_refs is an optional list of label name-value pair references, encoded
  // as indices to the Request.symbols array. This list's len is always
  // a multiple of 2, and the underlying labels should be sorted lexicographically.
  // If the exemplar references a trace it should use the `trace_id` label name, as a best practice.
  repeated uint32 labels_refs = 1;
  // value represents an exact example value. This can be useful when the exemplar
  // is attached to a histogram, which only gives an estimated value through buckets.
  double value = 2;
  // timestamp represents the timestamp of the exemplar in ms.
  // For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
  // for conversion from/to time.Time to Prometheus timestamp.
  int64 timestamp = 3;
}

// Sample represents series sample.
message Sample {
  // value of the sample.
  double value = 1;
  // timestamp represents timestamp of the sample in ms.
  int64 timestamp = 2;
}

// Metadata represents the metadata associated with the given series' samples.
message Metadata {
  enum MetricType {
    METRIC_TYPE_UNSPECIFIED    = 0;
    METRIC_TYPE_COUNTER        = 1;
    METRIC_TYPE_GAUGE          = 2;
    METRIC_TYPE_HISTOGRAM      = 3;
    METRIC_TYPE_GAUGEHISTOGRAM = 4;
    METRIC_TYPE_SUMMARY        = 5;
    METRIC_TYPE_INFO           = 6;
    METRIC_TYPE_STATESET       = 7;
  }
  MetricType type = 1;
  // help_ref is a reference to the Request.symbols array representing help
  // text for the metric. Help is optional, reference should point to an empty string in
  // such a case.
  uint32 help_ref = 3;
  // unit_ref is a reference to the Request.symbols array representing a unit
  // for the metric. Unit is optional, reference should point to an empty string in
  // such a case.
  uint32 unit_ref = 4;
}

// A native histogram, also known as a sparse histogram.
// See https://github.com/prometheus/prometheus/blob/remote-write-2.0/prompb/io/prometheus/write/v2/types.proto#L142
// for a full message that follows the native histogram spec for both sparse
// and exponential, as well as, custom bucketing.
message Histogram { ... }

所有时间戳必须是自 Unix 纪元以来的毫秒数,以 int64 计数。样本的值必须是 float64。

对于每个 TimeSeries 消息

  • labels_refs 必须提供。
  • sampleshistograms 中至少一个元素必须提供。一个 TimeSeries 不能同时包含 sampleshistograms。对于(极少)混合浮点和直方图样本的序列,必须使用单独的 TimeSeries 消息。
  • metadata 子字段应该提供。接收器可以拒绝 Metadata.type 未指定的序列。
  • 如果序列存在 Exemplars,则应该提供。
  • 对于遵循计数器语义的指标(例如计数器和直方图),created_timestamp 应该提供。接收器可以拒绝未设置 created_timestamp 的那些序列。

以下小节详细定义了一些 schema 元素。

符号

io.prometheus.write.v2.Request Protobuf 消息旨在通过 字符串驻留 来在标准压缩之上实现经证实的额外压缩和内存效率提升。

symbols 表必须提供,并且必须包含用于序列、Exemplar 标签和元数据字符串的去重字符串。symbols 表的第一个元素必须是空字符串,用于表示空或未指定的值,例如未提供 Metadata.unit_refMetadata.help_ref 时。引用必须指向 symbols 字符串数组中现有索引。

序列标签

每个 SampleHistogram 样本都必须发送完整的标签集。此外,与样本关联的标签集

  • 应该包含 __name__ 标签。
  • 不能包含重复的标签名称。
  • 标签名称必须按字典顺序排序。
  • 不能包含任何空的标签名称或值。

指标名称、标签名称和标签值必须是任意 UTF-8 字符序列。

指标名称应该遵循正则表达式 [a-zA-Z_:]([a-zA-Z0-9_:])*

标签名称应该遵循正则表达式 [a-zA-Z_]([a-zA-Z0-9_])*

不符合上述规范的名称可能对 PromQL 用户来说更难使用(详见 UTF-8 提案)。

以 “__” 开头的标签名称保留用于系统使用,不应该被使用,详见 Prometheus 数据模型

接收器也可以对标签的数量和长度施加限制,但这取决于具体的接收器实现,超出本文档的范围。

样本和直方图样本

发送器必须按时间戳顺序发送给定 TimeSeriessamples(或 histograms)。发送器可以并行发送不同序列的多个请求。

当时间序列不再追加时,发送器应该发送陈旧标记。如果可以检测到时间序列的停止,发送器必须发送陈旧标记,例如

  • 对于通过拉取(scrape)获取的序列,除非使用了显式时间戳。
  • 对于由记录规则评估产生的序列。

通常,不为已停止的序列发送陈旧标记可能导致接收器出现 复杂的查询时间对齐问题

陈旧标记必须通过特殊的 NaN 值 0x7ff0000000000002 来表示。此值不能用于其他情况。

通常,发送器可以使用以下技术检测时间序列何时不再追加

  1. 通过服务发现检测到暴露该序列的目标已消失。
  2. 注意到目标在连续的 scrape 之间不再暴露该时间序列。
  3. 未能 scrape 最初暴露时间序列的目标。
  4. 跟踪记录和告警规则的配置和评估。
  5. 跟踪非 scrape 来源指标的停止(例如,在 k6 中,当每个基准测试的序列完成时,它可以发出陈旧标记)。

元数据

元数据应该遵循 Prometheus 官方关于 类型(Type)帮助(Help) 的指南。

元数据可以遵循 OpenMetrics 官方关于 单位(Unit) 的指南。

Exemplars

每个 Exemplar,如果附加到 TimeSeries

  • 必须包含一个值。
  • 可以包含标签,例如引用跟踪或请求 ID。如果 Exemplar 引用了跟踪,作为最佳实践,它应该使用 trace_id 标签名称。
  • 必须包含一个时间戳。虽然 Exemplar 时间戳在 Prometheus/OpenMetrics 暴露格式中是可选的,但假设是在 scrape 时分配时间戳,就像为 scrape 样本分配时间戳一样。接收器需要 Exemplar 时间戳来可靠地处理(例如去重)传入的 Exemplar。

范围之外

1.0 相同。

未来计划

本节包含尚不属于协议规范但为完整性而在此提及的推测性计划。请注意,2.0 规范已完成了 1.0 中 3 个未来计划中的 2 个

  • 事务性(Transactionality) 2.0 规范中仍未定义事务性,主要是因为它使得可伸缩的发送器实现变得困难。Prometheus 发送器旨在实现“事务性”——即永不将部分抓取的目标暴露给查询。我们打算对 Remote-Write 采取同样的方法——例如,将来我们希望将 Remote-Write 与抓取“对齐”,使得单个抓取的所有样本、元数据和 Exemplar 都在一个 Remote-Write 请求中发送。

    然而,Remote-Write 2.0 规范解决了 经典直方图桶的一个重要事务性问题。这得益于原生直方图支持通过 io.prometheus.write.v2.Request 线格式进行自定义分桶。发送器可以以这种方式将所有经典直方图转换为原生直方图,但这超出了本规范的强制要求。然而,因此,接收器可以忽略某些指标类型(例如经典直方图)。

  • 替代线格式。OpenTelemetry 社区已通过其 OTLP 协议展示了 Apache Arrow(以及其他潜在的列式格式)在数据线传输方面的有效性。我们希望进行实验,以确认类似格式与 Prometheus 数据模型的兼容性,并包括任何资源使用变化的基准测试。出于兼容性原因,我们可能会长期维护 protobuf 和列式格式,并利用我们的内容协商为此目的添加不同的 Protobuf 消息。

  • 全局符号。用于字符串驻留的预定义字符串字典。该协议可以预定义一个静态的 ref->symbol 字典,其中包含被认为是常见的字符串,例如“namespace”、“le”、“job”、“seconds”、“bytes”等。发送器可以引用这些字符串,而无需将其包含在请求的符号表中。该字典可以随着此协议的次要版本发布而逐步增长。

常见问题

为什么没有使用 gRPC? 因为 1.0 协议没有使用 gRPC,打破这一点会增加采用的阻力。详见 1.0 的原因

为什么不流式传输 Protobuf 消息? 如果使用持久 HTTP/1.1 连接,它们非常接近流式传输。当然,标头必须重新发送,但这比建立新的 TCP 连接成本更低。

为什么我们要按顺序发送样本? 顺序约束源于我们在 Prometheus 中用于时间序列数据的编码,其实现针对仅追加工作负载进行了优化。然而,这个要求也在生态系统中的许多其他数据库和供应商中共享。事实上,启用了 OOO 功能的 Prometheus 允许乱序写入,但会带来性能损失,因此保留用于罕见事件。总而言之,接收器可能支持乱序写入,尽管规范不允许。未来,例如 2.x 规范版本中,如果需要,我们可以扩展内容类型来协商乱序写入。

如何在这种顺序约束下并行化请求? 样本必须按顺序 *针对给定序列*。然而,即使接收器不支持乱序写入,只要 Remote-Write 请求是针对不同序列的,它们就可以并行发送。Prometheus 将样本按其标签分片到不同的队列中,然后每个队列中的写入按顺序进行。这保证了同一序列的样本按顺序传递,但不同序列的样本可以并行发送——并且在不同序列之间可能“乱序”。

Remote-Write 2.0 和 OpenTelemetry 的 OTLP 协议有什么区别? OpenTelemetry OTLP 是一种用于在遥测源、中间节点和遥测后端之间传输遥测数据(例如指标、日志、跟踪和配置文件)的协议。推荐的传输方式是 gRPC 与 protobuf,但也描述了 HTTP 与 protobuf 或 JSON 的方式。它从零开始设计,旨在支持各种不同的可观测性信号、数据类型和额外信息。对于 指标 而言,这意味着额外的非识别标签、标志、时间聚合类型、资源或范围指标、schema URL 等。OTLP 还要求使用 语义约定

Remote-Write 的设计理念是简单性、效率和有机增长。其第一个版本于 2023 年正式发布,当时 CNCF 生态系统中已有 数十个经过实战检验的采用者 使用该协议多年。Remote-Write 2.0 在原有协议的基础上进行了迭代,增加了一些新元素(元数据、Exemplars、创建时间戳和原生直方图)和字符串驻留。Remote-Write 2.0 始终是无状态的,仅关注指标并具有明确的设计立场;因此,其范围限定在 Prometheus 社区认为足以构建健壮指标解决方案的元素。其目的是确保 Remote-Write 成为一个稳定的协议,比可观测性生态系统中的替代方案更便宜、更易于采用和使用。

本页内容