bigywater.tech
微服务 · 67

OTLP 与 Collector:gRPC、HTTP 两种传输,receiver、processor、exporter 流水线

OTLP 用 gRPC 还是 HTTP?gzip 能省多少?Collector 的 receiver、processor、exporter 流水线怎么接?batch、filter、resource 各做什么?下游挂了 Collector 能缓冲多少?这篇用 OpenTelemetry Python SDK 和 Collector 逐项量出来。

207 → 41 字节
每个 span 在 OTLP 里的字节数,不压缩与 gzip 之后(gRPC 与 HTTP 几乎一样)
40 → 10
有无 batch 处理器时,Collector 的 file exporter 写出次数(20000 个 span)
200 / 50515
下游停 10 s 时丢失的 span 数:应用 SDK 队列 200000 与默认 2048

传输与压缩

实测 · 本机执行输出
  传输方式           | 应用 → Collector 字节数 | 平均每个 span 的字节数 | Collector → 应用 字节数 | TCP 连接数 | 总耗时(s)
  ---------------+--------------------+----------------+--------------------+---------+-------
  gRPC           | 4,144,978          | 207.2          | 3,977              | 1       | 0.66  
  gRPC + gzip    | 816,179            | 40.8           | 4,351              | 1       | 0.72  
  HTTP(protobuf) | 4,148,711          | 207.4          | 4,600              | 1       | 0.65  
  HTTP + gzip    | 818,229            | 40.9           | 4,600              | 1       | 0.71  
  gRPC 和 HTTP 的负载都是同一个 protobuf 消息,所以不压缩时字节数接近;gzip 把属性里重复的键名和值压缩掉,体积大幅下降;gRPC 复用一条 HTTP/2 连接,HTTP 导出器(requests)的连接数见表

gRPC 和 HTTP 的负载都是 protobuf,不压缩时字节数几乎一样;gzip 把每个 span 压到约 41 字节。

处理器

实测 · 本机执行输出
  流水线                  | file exporter 写出的次数(行数) | span 数 | 平均每次写出的 span 数 | 写完全部 span 的时间(相对 SDK force_flush 返回,s) | 资源属性里的 deployment.*        
  ---------------------+-------------------------+--------+----------------+----------------------------------------+----------------------------
  (A)没有处理器             | 40                      | 20000  | 500            | 0.05                                   |                            
  (B)batch(2000 / 1 s) | 10                      | 20000  | 2000           | 1.04                                   |                            
  (C)resource + filter | 40                      | 16000  | 400            | 0.11                                   | deployment.environment=prod
  没有 batch 时 Collector 收到一批就写出一批(每批 512 个);batch 处理器把它们攒到 2000 个或 1 s 再写出,写出次数少、每次更大,代价是最多多等 timeout;filter 处理器丢弃了带 health=true 的 span

batch 把多次写出合成一次,多等 timeout;filter 丢弃指定的 span;resource 统一加资源属性。

下游故障

实测 · 本机执行输出
  下游停掉的时间 | 应用 SDK 的队列大小 | 下游最终收到的 span | 没收到   | Collector 日志 “Rejecting data” 条数 | Collector 日志 “Dropping data” 条数
  --------+--------------+--------------+-------+----------------------------------+--------------------------------
  3 s     | 200000       | 100000       | 0     | 2                                | 0                              
  10 s    | 200000       | 99800        | 200   | 5                                | 2                              
  10 s    | 2048         | 49485        | 50515 | 5                                | 2                              
  Collector 的 sending_queue 满了会拒收(“Rejecting data”),把压力还给应用的 SDK,由 SDK 的队列和重试继续缓冲;重试超过 max_elapsed_time 的那一批被丢弃(“Dropping data”)。所以丢多少取决于 Collector 队列、重试时间和 SDK 队列三层加起来能缓冲多久
ERRORS {}

Collector 的队列满了会拒收,把压力还给 SDK;丢多少取决于 Collector 队列、重试时间和 SDK 队列三层。

分步演示

OTLP 与 Collector
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。Collector contrib 0.163.1(otlp receiver、batch、resource、filter 处理器,file / nop / otlphttp exporter),OpenTelemetry Python SDK 1.45.1;字节数是用我写的 TCP 转发在应用与 Collector 之间量到的(含 TLS 以外的全部协议字节,没有加密);总耗时主要是 SDK 创建 span 的时间,不是传输性能的对比;每种配置只测了 1 次
    • 没有验证:TLS 的开销;gRPC 与 HTTP 在高并发、多连接下的吞吐差异;Collector 的 memory_limiter、persistent queue(file_storage)与 loadbalancing exporter;Collector 多实例、Agent + Gateway 两层部署;其他压缩算法(zstd);Collector 日志里 “Will retry” 在 info 级别,没有统计重试次数
    京ICP备2026067502号-1京公网安备11010502063487号