Tempo本身不支持Go微服务直接接入,必须通过OpenTelemetry Collector中转;Go服务需用OTLP HTTP exporter直连Collector,Collector配置须启用OTLP receiver和Tempo exporter、禁用采样与批处理,并调优连接数与资源限制以避免trace丢失。

Tempo 本身不支持直接作为 Go 微服务的 tracing 后端“接收器”——它只接收 trace 数据,不提供 OpenTracing / OpenTelemetry SDK 的原生 exporter。必须通过 otelcol-contrib(OpenTelemetry Collector)中转,且默认配置下无法处理秒级高频 trace 流量,需针对性调优。
Go 服务必须用 OpenTelemetry SDK,不能用 Jaeger 或 Zipkin client 直连 Tempo
Tempo 只暴露 /api/traces(Jaeger HTTP)、/opentelemetry/v1/trace(OTLP HTTP)等接口,但 Jaeger client 发送的是 POST /api/traces 的 Jaeger JSON 格式,而 Tempo 默认只启用 OTLP 接收器;Zipkin client 发送的 /api/v2/spans 也需显式开启 Zipkin receiver 才能接入。最稳路径是:Go 服务用 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp 直发 OTLP HTTP 到 Collector。
- 不要 import
jaeger或zipkin-go—— 它们与 Tempo 原生协议不匹配,容易出现 404 或解析失败 - 确保 Go SDK 初始化时指定
otlptracehttp.NewClient,endpoint 设为"http://otel-collector:4318/v1/traces"(不是 Tempo 地址) - 若误将 Go client endpoint 指向
tempo:3200,会收到404 page not found或unsupported media type
Collector 配置必须启用 OTLP receiver + Tempo exporter,并禁用采样
默认 Collector 配置不包含 Tempo exporter,且自带的 probabilistic 采样器会在高并发下丢 trace——秒级海量场景下必须关掉。Tempo 接收 trace 的关键路径是:OTLP → Collector → Tempo HTTP API,中间不能有采样、批处理延迟或队列积压。
- receiver 段必须启用
otlp(port4318),并关闭gzipcompression(Go SDK 默认不压缩,启用了反而 400) - exporter 段用
tempo/<code>(不是 <code>tempohttp),endpoint 设为"http://tempo:3200/otlp",注意路径后缀是/otlp - processor 段移除所有
batch、memory_limiter、probabilistic—— 秒级 trace 量大时 batch 会引入 1–10s 延迟,memory_limiter 会 drop spans - pipeline 中
exporters必须只写[tempo],不要混入logging或file,否则 CPU 和日志 IO 会拖慢吞吐
Tempo 自身需调大 gRPC 和 HTTP 并发连接数,否则 503 Service Unavailable
默认 Tempo 单实例只能承受约 500 QPS 的 trace 写入(按每秒 1000 span 计算)。当 Go 服务每秒产生 >1k trace 时,Collector 日志会出现 failed to export traces: rpc error: code = Unavailable desc = connection closed,Tempo 日志则报 http: Accept error: accept tcp: too many open files 或 grpc: Server.Serve failed to create TCP listener。
立即学习“go语言免费学习笔记(深入)”;
- 启动参数加
--server.grpc.max-connection-age=30m和--server.http.max-connections=10000 - Linux 系统需调高 ulimit:容器内执行
ulimit -n 65536,Docker run 加--ulimit nofile=65536:65536 - storage 配置若用
local(开发),务必设blocklist.poll-interval=1s,否则 block 扫描延迟导致 trace 查不到 - 生产必须用
gcs/s3/cassandra,且search.enabled=true,否则 /search 接口响应超时
Go SDK 的 Resource 和 Span 属性必须精简,避免单 trace 超过 1MB
Tempo 对单个 trace 大小无硬限制,但 Collector 默认 max_recv_msg_size=4194304(4MB),一旦 Go 服务在 span 中塞入完整 request body、SQL query、user agent 等长字符串,极易触发 rpc error: code = ResourceExhausted desc = grpc: received message larger than max。
- 禁用自动注入
http.request.body、db.statement全文——改用db.system+db.name+db.operation - Resource labels 控制在 5 个以内,如
service.name、service.version、deployment.environment,去掉host.name、os.type等冗余字段 - Span name 用静态字符串(如
"user.get_by_id"),不用拼接 ID 或时间戳,避免 cardinality 爆炸 - 测试时用
otel.WithSpanKind(trace.SpanKindServer)显式设 kind,避免 SDK 自动推导出错误类型影响 Tempo 分组
真正卡住的不是部署步骤,而是 Collector 到 Tempo 的 pipeline 是否全程无 buffer、无采样、无压缩,以及 Go 进程里每个 span 是否控制在 KB 级别——这两个点没调好,trace 就会静默丢失,查日志也看不出错在哪。


















