ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Loki 中的 prompb 包:深入解析 Prometheus Remote Read/Write 协议与 Protobuf 定义

Loki 中的 prompb 包:深入解析 Prometheus Remote Read/Write 协议与 Protobuf 定义 Loki 中的 prompb 包深入解析 Prometheus Remote Read/Write 协议与 Protobuf 定义【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki导读本文以 Loki 仓库内 vendored 的 prompb 包说明文档 为骨架结合其remote.proto、types.proto源码与 Loki 内的实际调用代码系统讲解 Prometheus 远程读写Remote Read/Write协议的消息模型、核心数据类型、序列化约定与稳定性保证。读完本文你将掌握WriteRequest、TimeSeries、ChunkedReadResponse等关键消息的结构与语义理解它们如何承载标签、样本、Exemplar、原生直方图等时序数据并能在 Loki 生态中正确构造或解析这些协议消息。一、prompb 包是什么目录定位与职责在 Loki 仓库中prompb 目录 是从 Prometheus 上游同步而来的 ProtobufProtocol Buffer定义集合专门服务于 Prometheus 的Remote Read远程读取与Remote Write远程写入协议。其核心使命是用 protobuf 对时序数据指标、标签、样本、查询请求等进行序列化/反序列化供网络通信使用保证 Loki以及任何采用该协议的组件与 Prometheus 之间、或经 Prometheus remote 协议对接的上下游系统之间的数据格式互通。该目录下的文件与buf.build公共 protobuf schema 注册表保持同步同步自 Prometheus 仓库的main分支。需要说明的是目录内还附带buf.yaml、buf.gen.yaml、buf.lock等 buf 工具链配置文件用于声明模块、锁定依赖并控制代码生成方式。目录内容清单文件作用remote.proto定义远程读/写服务相关的消息WriteRequest、ReadRequest、ChunkedReadResponse等types.proto跨协议共享的通用类型Label、TimeSeries、Sample、Histogram、MetricMetadata等remote.pb.go/types.pb.go由 protoc 生成的 Go 代码已纳入版本控制一般无需重新生成custom.go手工补充的扩展方法如ChunkedReadResponse.PooledMarshalcodec.go编解码辅助逻辑二、核心消息模型远程写入Remote Write远程写入是时序数据“离开采集端、进入存储端”的通道。其顶层消息定义在 remote.protomessage WriteRequest { repeated prometheus.TimeSeries timeseries 1; reserved 2; // Cortex 曾用它标识写请求来源此处保留以避免兼容性问题 repeated prometheus.MetricMetadata metadata 3; }关键点timeseries批量携带的时序数据主体每条TimeSeries是一个带标签的样本/直方图序列reserved 2字段 2 被显式保留。源码注释说明 Cortex 使用该字段判断写请求来源Prometheus 预留它以规避兼容性问题——这体现了跨项目协作时对协议演进空间的尊重metadata指标元数据类型、Help、单位的批量传输。TimeSeries标签与样本的容器TimeSeries定义在 types.protomessage TimeSeries { repeated Label labels 1; // 必填标签缺失时样本/Exemplar 无法被远端正确摄取 repeated Sample samples 2; repeated Exemplar exemplars 3; repeated Histogram histograms 4; }labels是必填字段——源码注释明确指出只有标签有效样本与 Exemplar 才能被远端系统正确摄取同一序列可同时携带普通浮点样本samples与原生直方图histogramsLabel就是最朴素的键值对message Label { string name 1; string value 2; }Sample 与 Exemplarmessage Sample { double value 1; int64 timestamp 2; // 单位毫秒ms } message Exemplar { repeated Label labels 1; // 可空 double value 2; int64 timestamp 3; // 单位毫秒ms }注意时间戳统一为毫秒ms这是 Prometheus 模型的标准约定源码注释指引转换逻辑参考model/timestamp包。MetricMetadata指标类型枚举types.proto 定义了与 Prometheus 一致的 8 种指标类型枚举值说明UNKNOWN未知类型COUNTER计数器GAUGE仪表可增可减HISTOGRAM直方图GAUGEHISTOGRAM仪表直方图SUMMARY摘要INFO信息型指标STATESET状态集合每个元数据包含metric_family_name指标族名、help帮助文本与unit单位三个字符串字段。三、远程读取Remote Read查询请求与响应协商远程读取协议允许 Prometheus或兼容系统向远端存储发起查询。核心消息在 remote.protomessage ReadRequest { repeated Query queries 1; enum ResponseType { SAMPLES 0; // 返回单条 ReadResponse内含原始样本 STREAMED_XOR_CHUNKS 1; // 流式返回 ChunkedReadResponse内含 XOR/HISTOGRAM 编码块 } repeated ResponseType accepted_response_types 2; }响应类型协商机制accepted_response_types以FIFO先入先出顺序列出客户端可接受的响应类型服务器按顺序尝试若列表为空则回退到SAMPLES类型 0若列表中的类型服务器均未实现则返回错误。两种响应类型的差异SAMPLES响应头为Content-Type: application/x-protobuf、Content-Encoding: snappy服务器一次性返回ReadResponseSTREAMED_XOR_CHUNKS响应头为Content-Type: application/x-streamed-protobuf; protoprometheus.ChunkedReadResponse服务器逐序列流式返回ChunkedReadResponse每个消息前带有 varint 长度前缀与 4 字节大端序 CRC32 Castagnoli 校验和推荐作为优先选择。Query 与 QueryResultmessage Query { int64 start_timestamp_ms 1; int64 end_timestamp_ms 2; repeated prometheus.LabelMatcher matchers 3; prometheus.ReadHints hints 4; } message QueryResult { repeated prometheus.TimeSeries timeseries 1; // 同一序列的样本必须按时间有序 } message ReadResponse { repeated QueryResult results 1; // 顺序与请求中的 queries 一一对应 }LabelMatcher支持四种匹配类型EQ等于、NEQ不等于、RE正则匹配、NRE正则不匹配ReadHints则携带查询优化提示包括步长step_ms、聚合函数字符串func、时间范围start_ms/end_ms、聚合标签组grouping、by标志及范围向量窗口range_ms。ChunkedReadResponse流式分块读取message ChunkedReadResponse { repeated prometheus.ChunkedSeries chunked_series 1; int64 query_index 2; // 对应 ReadRequest.queries 中的索引 }流式约定非常严格逐序列地流式传输可选按时间切分——一个数据帧可以只包含某序列的部分 chunk但一旦开始传输新序列旧序列就不会再补充 chunk序列返回顺序与 TSDB block 内部顺序一致。query_index用于标明这些 chunks 属于请求中的哪一条查询。ChunkedSeries与Chunk定义在 types.protomessage Chunk { int64 min_time_ms 1; int64 max_time_ms 2; // [min, max] 时间区间闭区间 enum Encoding { UNKNOWN 0; XOR 1; HISTOGRAM 2; FLOAT_HISTOGRAM 3; XOR2 4; HISTOGRAM_ST 5; FLOAT_HISTOGRAM_ST 6; } Encoding type 3; bytes data 4; } message ChunkedSeries { repeated Label labels 1; // 标签必须有序 repeated Chunk chunks 2; // chunk 按开始时间有序允许重叠 }四、通用类型深挖原生直方图与压缩编码Histogram稀疏直方图的紧凑表示原生直方图native histogram亦称稀疏直方图的消息定义在 types.proto一份消息可同时表达整数直方图与浮点直方图count与zero_count均为oneof可承载uint64整数计数或double浮点计数两种形态schema为sint32当前合法范围-4 n 8对应 base-2 桶结构——每个桶边界是前一边界乘以2^(2^-n)未来可能以 -4或 8扩展新桶模式zero_threshold表示零桶zero bucket的宽度负桶与正桶分别用negative_spans/positive_spansnegative_deltas/negative_counts、positive_spanspositive_deltas/positive_counts表示前者用于整数直方图的增量计数后者用于浮点直方图的绝对计数reset_hint枚举UNKNOWN/YES/NO/GAUGE辅助判断计数器重置场景custom_values字段明确标注不属于规范禁止在 remote write 客户端使用——它仅用于 Prometheus 内部从 OpenTelemetry 到 Prometheus 的转换。BucketSpantypes.proto是这种紧凑结构的关键offset表示与上一个 span 的间隔首个 span 可为负length表示连续桶的数量。把所有桶计数单独放在一个数组里、再用 span 描述连续段正是为了换取更紧凑的 protobuf 表示。Chunk.Encoding与 chunkenc.Encoding 对齐Chunk.Encoding枚举要求与 Prometheuschunkenc.Encoding保持一致覆盖XOR、HISTOGRAM、FLOAT_HISTOGRAM及带后缀_ST推测为 stream 相关变体的编码类型。这意味着远端存储收到的 chunk 数据可以直接按对应编码器解码无需二次转换。五、序列化约定与代码生成编解码与压缩与 Prometheus 生态其他协议一致remote read/write 的线上传输遵循固定套路用 gogo/protobuf 对消息Marshal得到二进制用snappy压缩通过 HTTP 携带Content-Type: application/x-protobuf与Content-Encoding: snappy传输。Loki 仓库内 pkg/logql/bench/cmd/correctness-metrics/push.go 给出了一个完整的、可复制的客户端示例它构造prompb.WriteRequest{Timeseries: series}经proto.Marshalsnappy.Encode后POST到 remote_write 端点并正确设置三个关键请求头httpReq.Header.Set(Content-Type, application/x-protobuf) httpReq.Header.Set(Content-Encoding, snappy) httpReq.Header.Set(X-Prometheus-Remote-Write-Version, 0.1.0)其中X-Prometheus-Remote-Write-Version: 0.1.0是 remote write 协议版本协商头代码还演示了 Basic Auth 鉴权、30 秒超时与 2xx 状态码校验是理解该协议落地细节的最佳参考实现。性能优化PooledMarshalvendor/github.com/prometheus/prometheus/prompb/custom.go 展示了消息类型上手工扩展的方法func (r *ChunkedReadResponse) PooledMarshal(p *sync.Pool) ([]byte, error) { size : r.Size() data, ok : p.Get().(*[]byte) if ok cap(*data) size { n, err : r.MarshalToSizedBuffer((*data)[:size]) ... return (*data)[:n], nil } return r.Marshal() }PooledMarshal利用sync.Pool复用缓冲区当池中缓冲容量足够时直接写入否则回退到普通Marshal。这种模式在高频流式响应场景如ChunkedReadResponse逐序列发送下能显著减少 GC 压力——这是从“协议定义”到“生产级实现”的典型工程化细节。代码生成流程按照 README 说明在仓库根目录执行make proto即可重新生成编译后的 protobuf 代码由于生成的 Go 代码remote.pb.go、types.pb.go已纳入版本控制日常使用无需重新生成除非修改了.proto定义。六、稳定性保证与演进策略这份 protobuf 定义遵循 Prometheus 项目的稳定性策略向后兼容的变更允许出现在 minor 版本中破坏性变更仅保留给 major 版本例如 Prometheus 3.0实验性/不稳定特性会在文档中明确标注。前述WriteRequest中reserved 2的保留、Histogram.custom_values的“非规范勿用”标注都是这一策略在具体字段上的体现。对于依赖该协议的实现方如 Loki 的 remote read/write 集成这意味着可以放心地把消息结构作为长期兼容的契约同时需关注 minor 版本间可能新增的可选字段——处理时应当容忍未知字段的存在。七、在 Loki 生态中的实际运用prompb 虽是 vendored 的第三方包但它在 Loki 中承担着真实的通信职责正确性基准测试Loki 的 metrics 正确性测试工具pkg/logql/bench/cmd/correctness-metrics/ 及 push.go直接以prompb.WriteRequest/prompb.TimeSeries构造写入流量把指标推送到 Prometheus remote_write 端点进行正确性校验协议互操作凡是 Loki 需要与 Prometheus remote 协议或兼容实现打交道的路径都共享这份消息定义从而保证标签、样本、Exemplar、直方图等数据的语义一致可复用参考若要在 Loki 中新增 remote read/write 能力或对接兼容存储remote.proto 与 types.proto 就是现成的消息契约custom.go 中的池化序列化模式也值得借鉴。结语prompb 包以极少的.proto定义支撑起了 Prometheus 远程读写协议的全部核心语义从WriteRequest的批量写入、ReadRequest的响应类型协商到ChunkedReadResponse的流式分块读取再到Histogram/BucketSpan的紧凑稀疏表示。理解这份协议契约是打通 Loki 与 Prometheus 生态数据链路、实现指标互操作的前提而 Loki 仓库中的 push.go 与 custom.go 则提供了从“读协议”到“写代码”的最佳范本。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表