Распределенная трассировка: от основ до production
15 / 1883%
Performance Tuning: минимизация overhead
50 минут
Performance Tuning: минимизация overhead
Проблема: overhead от трассировки
Реальный кейс:
Service: 10,000 req/sec
Без трассировки:
├─ CPU: 60%
├─ Memory: 512MB
└─ P95 latency: 50ms
С трассировкой (плохая конфигурация):
├─ CPU: 85% (+42%)
├─ Memory: 1.2GB (+135%)
└─ P95 latency: 65ms (+30%)Вопрос: Как снизить overhead до приемлемого уровня?
Цель: 2-5% CPU overhead, <1ms latency impact.
Измерение overhead
Метрики для мониторинга
1. CPU overhead:
# До включения трассировки
top -p $(pgrep -f node) # CPU: 60%
# После включения
top -p $(pgrep -f node) # CPU: 63% → overhead = 3% ✅2. Memory footprint:
// Мониторинг memory в приложении
const used = process.memoryUsage();
console.log({
heapUsed: Math.round(used.heapUsed / 1024 / 1024) + " MB",
heapTotal: Math.round(used.heapTotal / 1024 / 1024) + " MB",
});3. Latency impact:
P50 latency:
До: 25ms
После: 25ms (+0ms) ✅
P95 latency:
До: 50ms
После: 51ms (+1ms) ✅
P99 latency:
До: 100ms
После: 103ms (+3ms) ⚠️ ПриемлемоБатчинг spans (критически важно!)
Проблема: Синхронная отправка каждого span блокирует thread.
// ❌ Плохо: каждый span → HTTP request
span.end();
→ HTTP POST to Jaeger (5ms network latency)Решение: BatchSpanProcessor — отправка пакетами.
const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const {
OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-http");
const exporter = new OTLPTraceExporter({
url: "http://otel-collector:4318/v1/traces",
});
const batchProcessor = new BatchSpanProcessor(exporter, {
maxQueueSize: 2048,
maxExportBatchSize: 512,
scheduledDelayMillis: 5000,
exportTimeoutMillis: 30000,
});
provider.addSpanProcessor(batchProcessor);Результат:
До: 1000 spans → 1000 HTTP requests (5s network time)
После: 1000 spans → 2 HTTP requests по 500 spans (10ms)Экономия: 99.8% сокращение network overhead!
Оптимальная production-конфигурация
const { NodeSDK } = require("@opentelemetry/sdk-node");
const {
getNodeAutoInstrumentations,
} = require("@opentelemetry/auto-instrumentations-node");
const {
OTLPTraceExporter,
} = require("@opentelemetry/exporter-trace-otlp-http");
const {
BatchSpanProcessor,
ParentBasedSampler,
TraceIdRatioBasedSampler,
} = require("@opentelemetry/sdk-trace-base");
const { Resource } = require("@opentelemetry/resources");
const {
SemanticResourceAttributes,
} = require("@opentelemetry/semantic-conventions");
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: "api-gateway",
[SemanticResourceAttributes.SERVICE_VERSION]: "1.0.0",
});
const exporter = new OTLPTraceExporter({
url: "http://otel-collector:4318/v1/traces",
headers: {
"Content-Encoding": "gzip",
},
});
const batchProcessor = new BatchSpanProcessor(exporter, {
maxQueueSize: 2048,
maxExportBatchSize: 512,
scheduledDelayMillis: 5000,
exportTimeoutMillis: 30000,
});
const sdk = new NodeSDK({
resource: resource,
spanProcessor: batchProcessor,
sampler: new ParentBasedSampler({
root: new TraceIdRatioBasedSampler(0.01),
}),
instrumentations: [
getNodeAutoInstrumentations({
"@opentelemetry/instrumentation-http": {
ignoreIncomingPaths: ["/health", "/metrics"],
},
"@opentelemetry/instrumentation-fs": {
enabled: false,
},
}),
],
spanLimits: {
attributeValueLengthLimit: 256,
attributeCountLimit: 128,
},
});
sdk.start();Best Practices Summary
1. Всегда используйте BatchSpanProcessor
2. Начните с 1-5% sampling
3. Включите GZIP compression
4. Фильтруйте health checks
5. Мониторьте overhead
6. Используйте OTel Collector для offloading
Следующий урок
В следующем уроке мы изучим Security & Privacy — защита чувствительных данных в traces.
Теперь вы можете минимизировать overhead до 2-3% CPU!