OpenTelemetry 性能优化

OpenTelemetry 设计时充分考虑了性能,但在高负载场景下,合理的配置可以进一步降低开销。本文档介绍性能优化的最佳实践。

性能影响评估

典型性能开销

  • CPU 开销: < 5%
  • 内存开销: 10-50MB(取决于负载和配置)
  • 网络开销: 可通过采样控制
  • 延迟影响: < 1ms

性能瓶颈

  1. Span 导出: 网络 I/O
  2. 属性收集: 内存分配
  3. 采样决策: CPU 计算
  4. 上下文传播: 内存复制

优化策略

1. 采样优化

降低采样率

# 高流量服务:1% 采样
export OTEL_TRACES_SAMPLER_ARG=0.01
# 中等流量:10% 采样
export OTEL_TRACES_SAMPLER_ARG=0.1
# 低流量:50% 采样
export OTEL_TRACES_SAMPLER_ARG=0.5

智能采样

只采样关键路径:

sampler := NewCustomSampler(
0.01, // 默认 1%
[]string{"/checkout", "/payment"}, // 关键路径 100%
[]string{"/health", "/metrics"}, // 健康检查 0%
)

2. 批量导出优化

调整批量大小

# 增加批量大小,减少网络请求
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=1024
# 增加批量超时,等待更多数据
export OTEL_BSP_SCHEDULE_DELAY=10000 # 10 秒

Go 代码配置:

sdktrace.WithBatcher(exporter,
sdktrace.WithMaxExportBatchSize(1024),
sdktrace.WithBatchTimeout(10*time.Second),
sdktrace.WithMaxQueueSize(4096),
)

Python 代码配置:

BatchSpanProcessor(
exporter,
max_queue_size=4096,
max_export_batch_size=1024,
export_timeout_millis=30000,
schedule_delay_millis=10000,
)

队列大小

# 增加队列大小,避免丢数据
export OTEL_BSP_MAX_QUEUE_SIZE=4096

注意: 队列太大会增加内存使用。

3. 压缩优化

启用 gzip 压缩

export OTEL_EXPORTER_OTLP_COMPRESSION=gzip

Go 代码配置:

otlptracegrpc.WithCompressor("gzip")

Python 代码配置:

OTLPSpanExporter(
compression=Compression.GZIP,
)

效果: 可减少 50-80% 的网络带宽。

4. 属性限制

限制属性数量

export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=64 # 默认 128

Go 代码配置:

sdktrace.WithSpanLimits(sdktrace.SpanLimits{
AttributeCountLimit: 64,
})

限制属性值长度

export OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT=512 # 默认 无限制

Go 代码配置:

sdktrace.WithSpanLimits(sdktrace.SpanLimits{
AttributeValueLengthLimit: 512,
})

避免记录大对象

// ❌ 错误:记录大对象
span.SetAttributes(
attribute.String("request.body", largeJSONString), // 可能很大
)
// ✅ 正确:只记录摘要
span.SetAttributes(
attribute.Int("request.body.size", len(largeJSONString)),
attribute.String("request.body.type", "json"),
)

5. 禁用不必要的埋点

只启用需要的埋点库

# 只启用需要的埋点
export OTEL_INSTRUMENTATION_GIN_ENABLED=true
export OTEL_INSTRUMENTATION_REDIS_ENABLED=true
export OTEL_INSTRUMENTATION_DATABASE_SQL_ENABLED=false

6. 异步导出

确保导出是异步的,不阻塞主流程:

Go 示例:

// ✅ 正确:使用 BatchSpanProcessor(异步)
sdktrace.WithBatcher(exporter)
// ❌ 错误:使用 SimpleSpanProcessor(同步)
sdktrace.WithSpanProcessor(sdktrace.NewSimpleSpanProcessor(exporter))

7. 资源属性优化

只设置必要的资源属性

// ✅ 正确:只设置必要的属性
resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
),
)
// ❌ 错误:设置过多属性
resource.New(ctx,
resource.WithAttributes(
semconv.ServiceName("my-service"),
semconv.ServiceVersion("1.0.0"),
attribute.String("custom.attr1", "value1"),
attribute.String("custom.attr2", "value2"),
// ... 很多不必要的属性
),
)

8. 上下文传播优化

使用高效的传播器

# 只使用必要的传播器
export OTEL_PROPAGATORS=tracecontext,baggage

Go 代码配置:

otel.SetTextMapPropagator(
propagation.NewCompositeTextMapPropagator(
propagation.TraceContext{}, // 必需
propagation.Baggage{}, // 可选
),
)

9. Metrics 优化

调整导出间隔

# 增加导出间隔,减少网络请求
export OTEL_METRIC_EXPORT_INTERVAL=120000 # 2 分钟

Go 代码配置:

metric.NewPeriodicReader(exporter,
metric.WithInterval(2*time.Minute),
)

使用 Observable 指标

对于频繁变化的指标,使用 Observable 而不是直接记录:

// ✅ 正确:使用 Observable
meter.Int64ObservableGauge("cpu.usage", func(ctx context.Context, o metric.Observer) error {
o.ObserveInt64(cpuGauge, getCPUUsage())
return nil
})
// ❌ 错误:频繁记录
for {
cpuCounter.Add(ctx, getCPUUsage())
time.Sleep(1 * time.Second)
}

10. 日志优化

限制日志导出

# 只导出错误级别以上的日志
export OTEL_LOG_LEVEL=error

批量导出日志

export OTEL_BLRP_MAX_EXPORT_BATCH_SIZE=512
export OTEL_BLRP_SCHEDULE_DELAY=2000

性能测试

基准测试

func BenchmarkTracing(b *testing.B) {
ctx := context.Background()
tracer := otel.Tracer("benchmark")
b.ResetTimer()
for i := 0; i < b.N; i++ {
ctx, span := tracer.Start(ctx, "operation")
span.SetAttributes(attribute.String("key", "value"))
span.End()
}
}

性能监控

监控以下指标:

  • CPU 使用率: 应该 < 5%
  • 内存使用: 应该 < 50MB
  • 网络带宽: 根据采样率调整
  • 延迟影响: 应该 < 1ms

压力测试

# 使用 wrk 进行压力测试
wrk -t12 -c400 -d30s http://localhost:8080/
# 监控 OpenTelemetry 性能
# - CPU 使用率
# - 内存使用
# - 网络流量

性能调优检查清单

  • 采样率是否合理(1-10%)
  • 批量导出配置是否优化
  • 是否启用了压缩
  • 属性数量是否有限制
  • 是否禁用了不必要的埋点
  • 导出是否是异步的
  • 资源属性是否精简
  • 传播器配置是否高效
  • Metrics 导出间隔是否合理
  • 日志级别是否适当

性能问题排查

CPU 使用率过高

可能原因:

  • 采样率过高
  • 同步导出
  • 属性处理过多

解决方案:

# 降低采样率
export OTEL_TRACES_SAMPLER_ARG=0.01
# 确保使用批量导出
# 检查是否有同步导出器

内存使用过高

可能原因:

  • 队列积压
  • 属性过多
  • 资源属性过多

解决方案:

# 减小队列大小
export OTEL_BSP_MAX_QUEUE_SIZE=1024
# 限制属性数量
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=64

网络带宽过高

可能原因:

  • 采样率过高
  • 未启用压缩
  • 属性值过大

解决方案:

# 降低采样率
export OTEL_TRACES_SAMPLER_ARG=0.01
# 启用压缩
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
# 限制属性值长度
export OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT=512

延迟增加

可能原因:

  • 同步导出
  • 属性处理过多
  • 上下文传播开销

解决方案:

  • 确保使用异步批量导出
  • 减少属性数量
  • 优化上下文传播

生产环境推荐配置

# 服务信息
export OTEL_SERVICE_NAME=my-service
export OTEL_RESOURCE_ATTRIBUTES=service.version=1.0.0,deployment.environment=production
# OTLP Exporter
export OTEL_EXPORTER_OTLP_ENDPOINT=uptrace-grpc.yodao.cn:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
export OTEL_EXPORTER_OTLP_HEADERS=uptrace-dsn=http://token@uptrace-grpc.yodao.cn/project
export OTEL_EXPORTER_OTLP_COMPRESSION=gzip
export OTEL_EXPORTER_OTLP_TIMEOUT=15000
# Traces
export OTEL_TRACES_EXPORTER=otlp
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1 # 10% 采样
# Batch Processor
export OTEL_BSP_MAX_QUEUE_SIZE=2048
export OTEL_BSP_MAX_EXPORT_BATCH_SIZE=512
export OTEL_BSP_SCHEDULE_DELAY=5000
export OTEL_BSP_EXPORT_TIMEOUT=30000
# Span 限制
export OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT=128
export OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT=1024
# Metrics
export OTEL_METRICS_EXPORTER=otlp
export OTEL_METRIC_EXPORT_INTERVAL=60000
# Logs
export OTEL_LOGS_EXPORTER=otlp
export OTEL_LOG_LEVEL=warn
# Propagators
export OTEL_PROPAGATORS=tracecontext,baggage

下一步

参考资源

在Gitlab上编辑本文档