ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Nightingale pushgw 纯转发 Remote Write 代理:/proxy/v1/write 接口全解析

2026/9/15 13:01:22 拓冰建站 浏览量
Nightingale pushgw 纯转发 Remote Write 代理:/proxy/v1/write 接口全解析 Nightingale pushgw 纯转发 Remote Write 代理/proxy/v1/write 接口全解析【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingaleNightingale 的 pushgw 组件提供了一条**纯转发pure forwarding**的 Prometheus remote_write 写入通道客户端将 remote_write 数据推送到 pushgwpushgw 不做任何解析、不经过内存队列直接把原始字节流转发给配置文件中Writers列表里的每个后端Prometheus / VictoriaMetrics / Mimir 或任何支持 remote_write 协议的存储。本文基于 doc/api/pushgw-proxy-write.md 及仓库源码完整讲解该接口的认证、透传规则、背压限流、Writers 配置、监控指标与客户端接入方式并给出与/prometheus/v1/write的选型对比。读完本文你将掌握如何把 pushgw 当作带认证与并发保护的 L7 反向代理来构建多数据中心、多副本扇出的指标写入链路。一、设计定位为什么需要一条纯转发通道pushgw 原有的/prometheus/v1/write路径会走完整的**内存队列 relabel 分片sharding**流水线pushgw 需要解码 protobuf、按业务组和 target 归属重新组织数据、做标签改写再分发到各 writer。这在大多数场景下是正确的选择但也有一些场景希望 pushgw 退化为一个透明的搬运工pushgw 只作为采集接入网关ingest gateway写入聚合write aggregation完全交给后端集群完成需要多数据中心 / 多副本扇出fan-out把同一份数据复制到多个后端希望客户端的请求头Content-Encoding、X-Prometheus-Remote-Write-Version等原样保留而不是被 pushgw 重新封装。/proxy/v1/write正是为此设计。从源码注释可以看到其定位这个方法中pushgw 不做任何处理不解析 http request body直接转发给配置文件中指定的多个 writers。相比/prometheus/v1/write方法这个方法不需要在内存里搞很多队列性能更好见 pushgw/router/router_proxy_remotewrite.go。它的行为更接近一个带认证和并发保护的 L7 反向代理。二、接口端点与认证2.1 端点定义POST /proxy/v1/write请求体标准的 Prometheus remote_write 格式即protobuf snappy 压缩pushgw从不解析 body原样转发请求 URL 的query string 也会逐字拼接到每个 writer 的 URL 上是否需要认证由 pushgw 配置决定见下文BasicAuth。2.2 认证开关是否启用认证由pushgw.yaml中的HTTP.APIForAgent.BasicAuth/HTTP.APIForService.BasicAuth控制任一 map非空→ 启用 HTTP Basic Auth请求必须携带Authorization: Basic base64(user:pass)两者都为空→ 无需认证。从路由注册代码可见/proxy/v1/write与/prometheus/v1/write、/opentsdb/put、/openfalcon/push共用同一套认证逻辑配置了 BasicAuth 时统一挂上认证中间件未配置时直接开放见 pushgw/router/router.go。示例请求curl -u myuser:mypass \ -H Content-Type: application/x-protobuf \ -H Content-Encoding: snappy \ -H X-Prometheus-Remote-Write-Version: 0.1.0 \ --data-binary payload.snappy \ http://pushgw:17000/proxy/v1/writepushgw:17000是默认的 pushgw HTTP 服务地址端口可在etc/config.toml的[HTTP]段配置。三、Header 透传规则与 Query String 拼接3.1 Header 透传pushgw 向 writer 转发时会设置以下四个请求头客户端未携带时使用默认值Header默认值说明Content-Typeapplication/x-protobufremote_write 标准类型Content-Encodingsnappyremote_write 标准压缩User-Agentn9e若客户端传了 UA则追加-n9e后缀如prometheus/2.45.0-n9eX-Prometheus-Remote-Write-Version0.1.0remote_write 协议版本除上述四个头之外其他请求头一律不透传。writer 自身的BasicAuthUser/BasicAuthPass/Headers会在转发时单独设置来自配置文件见下文。这一点在源码中有直接体现forwardToWriter中只req.Header.Set了这四个字段随后单独处理 writer 的 BasicAuth 与自定义 Headers见 pushgw/router/router_proxy_remotewrite.go。3.2 Query String 透传请求 URL 的 query string 会被逐字拼接到每个 writer 的 URL 之后。例如Writer 配置http://vminsert:8480/insert/0/prometheus/api/v1/write客户端请求POST /proxy/v1/write?extra_labelcluster%3Dcn-bj实际转发POST http://vminsert:8480/insert/0/prometheus/api/v1/write?extra_labelcluster%3Dcn-bj如果 writer URL 本身已包含?则用衔接。源码实现为strings.Contains(w.Url, ?)分支选择拼接符见 pushgw/router/router_proxy_remotewrite.go。这个特性让 VictoriaMetrics 风格的extra_label参数可以直接透传十分实用。四、响应语义成功的提前返回与失败码4.1 成功响应HTTP/1.1 200 OK注意只要 pushgw 收到数据并准备好转发就立即返回 200。后端 writer 是否真正写入成功包括 4xx/5xx、超时、连接失败只反映在日志和指标中绝不会反向传播到客户端响应。这是多 writer 扇出设计的必然结果——一个慢的或挂掉的后端不能拖垮整个请求。测试用例TestProxyConcurrentForwardFaultIsolation明确验证了这一点其中一个 writer 返回 500 时其余 writer 仍正常收到数据、客户端仍得到 200见 pushgw/router/router_proxy_remotewrite_test.go。4.2 失败响应状态码响应体触发条件400{error: ...}读取 body 失败连接重置、客户端提前关闭等413proxy remote write body too large: N bytes单请求 body 超过ProxyMaxBodyBytes429proxy remote write inflight over limit: N并发 in-flight 请求数超过ProxyInflightMax429 是一种背压backpressure信号。配合 remote_write 客户端原生的 WAL 与退避重试机制客户端会自动重试上层无需任何额外处理。这正是该接口的背压哲学把缓冲责任交回客户端而不是在 pushgw 内存里堆积。五、背压与限流两个全局参数/proxy/v1/write用两个全局参数限制内存占用配置项默认值说明Pushgw.ProxyInflightMax1000单 pushgw 进程的并发上限。超限立即返回 429且该请求不进入writer 转发流程Pushgw.ProxyMaxBodyBytes32 * 1024 * 102432 MiB单请求 body 的最大字节数超限返回 413在配置结构pconf.Pushgw中这两个字段的注释写明了设计意图ProxyInflightMax 控制 /proxy/v1/write 的并发上限……把背压交给客户端 WALremote_write 协议原生支持、ProxyMaxBodyBytes …… 和 ProxyInflightMax 配套并发 × 单请求大小 pushgw 内存占用上限见 pushgw/pconf/conf.go。PreCheck()中 0时回落到默认值 1000 / 32 MiB。示例pushgw.yamlPushgw: ProxyInflightMax: 2000 ProxyMaxBodyBytes: 67108864 # 64 MiB Writers: - Url: http://victoriametrics-1:8428/api/v1/write Timeout: 10000 DialTimeout: 3000 MaxIdleConns: 100 MaxIdleConnsPerHost: 100 IdleConnTimeout: 90000 Headers: - X-Scope-OrgID - n9e - Url: http://victoriametrics-2:8428/api/v1/write BasicAuthUser: writer BasicAuthPass: secret Timeout: 10000内存天花板粗略为ProxyInflightMax × ProxyMaxBodyBytes即约 1000 × 32 MiB ≈ 32 GiB但实际峰值远低于此——大多数 remote_write 批次只有 64–256 KiB。5.1 实现细节CAS 闸门与 buffer 池源码中的背压实现有几个值得注意的工程细节见 pushgw/router/router_proxy_remotewrite.goCAS 抢占 in-flight 槽位用CompareAndSwap而不是先 Add 再检查被拒请求不会把计数短暂推到 maxNgauge 不会出现毛刺全局 atomic 计数proxyInflight是进程内共享的原子变量多实例 pushgw 各自独立限流buffer pool 复用proxyBodyBufPool复用读取缓冲避免高 QPS 下每请求都 make 大 sliceproxyBodyBufMaxCap 4 MiB过大的 buffer 不回收防止长期占用内存拒绝路径的小量 drain429/413 时只 drainproxyDrainOnRejectBytes 64 KiB既能保住 keep-alive又避免被拒请求反而消耗大量 IO 加重过载——若Content-Length很大全量 drain 会变成 DoS 放大器body 大小判定用io.LimitReader(c.Request.Body, maxBody1)多读 1 字节用于区分刚好等于上限和超过上限。5.2 转发模式串行与并行同一份 body 需要扇出到多个 writer 时默认串行转发依次调用每个 writer。当配置ProxyConcurrentForward: true且 writer 数量大于 1 时改为并行转发把单请求耗时从 sum(latency) 降到 max(latency)缩短 in-flight 槽位持有时间缓解慢 writer 拖累健康 writer见 pushgw/pconf/conf.go。并行分支的健壮性在测试中有充分覆盖见 pushgw/router/router_proxy_remotewrite_test.goTestProxyConcurrentForwardCorrectness3 个 writer 各收到一份且 body 字节完全一致TestProxyConcurrentForwardSpeedup每个 writer sleep 200ms 时并行整体耗时约 200ms、串行约 600msTestProxyConcurrentForwardFaultIsolation坏 writer返回 500不影响好 writer客户端仍得 200TestProxyConcurrentForwardNoDataRace并发 50 个请求在-race下无数据竞争TestProxyConcurrentSingleWriter单 writer 时即便开启开关也走串行分支。值得注意的是无论串并行handler 都会等所有转发结束再返回——因为 body 字节复用自 buffer pool提前归还会让仍在读取它的 goroutine 与下个请求竞争同一块内存。另外并行分支的每个子 goroutine 都做了recover兜底避免单个 writer 的 panic 拖垮整个进程。六、Writers 配置详解每个 writer 的字段如下对应pconf.WriterOptions见 pushgw/pconf/conf.go字段类型说明Urlstring后端 remote_write 地址必填BasicAuthUser/BasicAuthPassstring后端 BasicAuth 凭证Timeoutint (ms)整请求超时0时默认 10000DialTimeoutint (ms)TCP 连接超时默认 3000MaxConnsPerHost/MaxIdleConns/MaxIdleConnsPerHostintHTTP 连接池参数IdleConnTimeout/KeepAlive/TLSHandshakeTimeout/ExpectContinueTimeoutint (ms)各类 HTTP transport 超时Headers[]string自定义请求头按[key1, val1, key2, val2, ...]成对书写若 key 为Host则同时设置req.HostAsyncWritebool若有多个转发 writer对不重要的 writer 可置 true 异步转发提高转发效率几个与实现强相关的细节超时不能为 0PreCheck中若Timeout 0会被强制设为 10000因为 0 会被透传成 transport 的无超时端点挂死时写出 goroutine 会永久卡住见 pushgw/pconf/conf.goHTTP transport 启动时预初始化writer 在配置文件中写死、不支持动态更新所以启动时就把http.Transport含连接池、TLS 配置初始化好见 pushgw/pconf/conf.go支持 TLSUseTLS/TLSCA/TLSCert/TLSKey/InsecureSkipVerify等可配置来自内嵌的tlsx.ClientConfig。重要限制/proxy/v1/write不使用WriteRelabels——它不解析 body自然无法 relabel。relabel 只对/prometheus/v1/write路径生效。etc/config.toml中的对应示例段[[Pushgw.Writers]]还展示了完整的可选项包括 TLS 配置与 WriteRelabels 示例见 etc/config.toml。七、监控指标与推荐告警pushgw 在/metrics上暴露以下指标命名空间n9e_pushgw定义见 pushgw/pstat/pstat.go指标类型Labels说明n9e_pushgw_proxy_remote_write_totalCounter-/proxy/v1/write请求总数n9e_pushgw_proxy_remote_write_inflightGauge-当前 in-flight 请求数观察背压的关键指标n9e_pushgw_proxy_remote_write_over_limit_totalCounter-因超过并发上限被 429 拒绝的请求数n9e_pushgw_proxy_remote_write_body_too_large_totalCounter-因超过 body 上限被 413 拒绝的请求数n9e_pushgw_proxy_forward_totalCounterurl对每个 writer 发出的转发次数n9e_pushgw_proxy_forward_error_totalCounterurl,reason转发失败次数reason取值为build_request/do_request/status_4xx_5xxn9e_pushgw_proxy_forward_duration_secondsHistogramurl单次转发延迟分布bucket.001/.01/.1/1/5/10 秒转发错误的三种reason对应源码中的三个统计点请求构造失败http.NewRequest出错、请求执行失败client.Do出错、后端返回 4xx/5xx见 pushgw/router/router_proxy_remotewrite.go。推荐告警n9e_pushgw_proxy_remote_write_inflight长期接近ProxyInflightMax→ 扩容或提高阈值rate(n9e_pushgw_proxy_remote_write_over_limit_total[5m]) 0持续触发 → 后端写入慢客户端可能丢数据rate(n9e_pushgw_proxy_forward_error_total[5m]) 0→ 按url/reason维度拆分定位问题。八、客户端配置示例8.1 Prometheusremote_write: - url: http://pushgw:17000/proxy/v1/write basic_auth: username: myuser password: mypass queue_config: capacity: 10000 max_shards: 50 max_samples_per_send: 2000queue_config的合理调优提高max_shards/max_samples_per_send能充分发挥客户端 WAL 的缓冲能力让 429 背压场景下的重试更加平滑。8.2 vmagentvmagent \ -remoteWrite.urlhttp://pushgw:17000/proxy/v1/write \ -remoteWrite.basicAuth.usernamemyuser \ -remoteWrite.basicAuth.passwordmypass8.3 Grafana Alloy / OpenTelemetry Collector任何支持 Prometheus remote_write 协议的客户端都可以直接接入无需特殊适配。九、与/prometheus/v1/write的对比与选型维度/prometheus/v1/write/proxy/v1/write解析 body是protobuf 解码、relabel、分片否原始字节转发内存队列大型多分片内存队列仅 in-flight 计数Relabel / drop / 标签改写支持不支持心跳元数据更新支持不支持Kafka writer支持不支持背压机制队列水位 丢弃in-flight 阈值 429延迟 / CPU 开销较高极低最佳适用pushgw 内部做处理或路由pushgw 只做认证 扇出选型经验法则很简单需要 relabel、target 心跳或 Kafka 旁路→ 用/prometheus/v1/write只想透明转发到 remote_write 后端→ 用/proxy/v1/write。十、总结/proxy/v1/write是 Nightingale pushgw 在完整处理流水线之外提供的一条轻量直通通道它用 in-flight CAS 闸门 body 大小限制做背压把内存占用控制在ProxyInflightMax × ProxyMaxBodyBytes的天花板内header 与 query string 的透传规则让 VictoriaMetrics 的extra_label等能力得以无损使用多 writer 扇出时先回 200、失败只进指标的语义天然适配多数据中心复制场景。结合仓库中的实现与测试pushgw/router/router_proxy_remotewrite.go、pushgw/router/router_proxy_remotewrite_test.go你可以放心地把 pushgw 当作高吞吐的 remote_write 接入网关来使用。【免费下载链接】nightingaleNightingale is to monitoring and alerting what Grafana is to visualization.项目地址: https://gitcode.com/GitHub_Trending/ni/nightingale创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考