Ответ
Трассировка (Distributed Tracing) — это механизм отслеживания запроса через все сервисы распределенной системы. Ключевые концепции: Trace (весь путь запроса), Span (отдельная операция), Context Propagation (передача идентификаторов).
1. Основные компоненты трассировки:
Trace (traceId: abc123)
├── Span 1 (spanId: 1, service: Gateway, operation: auth) [50ms]
│ └── Span 2 (spanId: 2, service: AuthService, operation: validate) [30ms]
└── Span 3 (spanId: 3, service: OrderService, operation: create) [120ms]
├── Span 4 (spanId: 4, service: PaymentService, operation: charge) [80ms]
└── Span 5 (spanId: 5, service: InventoryService, operation: reserve) [40ms]
2. Реализация с Spring Cloud Sleuth + Zipkin:
# application.yml
spring:
sleuth:
sampler:
probability: 1.0 # Процент трассируемых запросов (1.0 = 100%)
zipkin:
base-url: http://localhost:9411
sender:
type: web # Отправка через HTTP
// Автоматическое создание span в контроллере
@RestController
@Slf4j
public class OrderController {
private final Tracer tracer; // Инжектируется Sleuth
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
// Создание кастомного span
Span customSpan = tracer.nextSpan().name("validateOrderRequest").start();
try (SpanInScope ws = tracer.withSpan(customSpan)) {
// Логи с traceId и spanId
log.info("Validating order request for user: {}", request.getUserId());
// Бизнес-логика
Order order = orderService.create(request);
// Добавление тегов для аналитики
customSpan.tag("order.amount", request.getAmount().toString());
customSpan.tag("user.tier", request.getUserTier());
return ResponseEntity.ok(order);
} finally {
customSpan.end();
}
}
// Асинхронная трассировка
@Async
public CompletableFuture<Void> asyncOperation() {
// Sleuth автоматически передает контекст в @Async методы
log.info("Async operation with traceId");
return CompletableFuture.completedFuture(null);
}
}
3. Контекст и propagation:
// Ручная передача контекста между потоками
Span span = tracer.currentSpan();
TraceContext context = span.context();
String traceId = context.traceId();
String spanId = context.spanId();
// Для передачи вручную (например, в кастомный executor)
TraceContext traceContext = tracer.currentTraceContext().context();
executor.submit(() -> {
try (TraceContext.Scope scope = tracer.currentTraceContext().newScope(traceContext)) {
// Код с унаследованным контекстом трассировки
log.info("Executing in thread with traceId: {}", traceId);
}
});
4. Интеграция с другими инструментами:
// Добавление traceId в HTTP-заголовки для внешних вызовов
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
// Sleuth автоматически добавляет заголовки X-B3-TraceId, X-B3-SpanId
restTemplate.setInterceptors(Collections.singletonList(
new RestTemplateInterceptor()
));
return restTemplate;
}
// Интеграция с Messaging (Kafka)
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> config = new HashMap<>();
config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
// Добавление инжектора контекста для Kafka
return new DefaultKafkaProducerFactory<>(config,
new StringSerializer(),
new StringSerializer(),
new PropagatingSenderInterceptor<>() // Передает trace контекст в headers
);
}
5. Best Practices:
- Сэмплинг: На production использовать adaptive sampling (например, 10-20% запросов)
- Теги: Добавлять business-теги (userId, orderId, endpoint) для поиска
- Производительность: Использовать асинхронную отправку spans в collector
- Корреляция: Связывать трассировку с логами через MDC и метриками
- Алертинг: Настраивать алерты на медленные traces (по percentiles: p95, p99)