Skip to content

Spring Cloud 链路追踪

1. 链路追踪概述

1.1 为什么需要链路追踪

  • 故障定位:快速定位问题所在
  • 性能分析:分析调用链性能瓶颈
  • 依赖分析:了解服务间依赖关系
  • 调用可视化:直观展示调用链路

1.2 核心概念

概念说明
Trace一次完整请求的追踪链路
Span追踪链路中的一个节点
AnnotationSpan 中的事件标记
TraceId全局唯一的追踪 ID
SpanIdSpan 的唯一标识

2. Micrometer Tracing

2.1 添加依赖

xml
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
</dependency>

2.2 配置追踪

yaml
management:
  tracing:
    enabled: true
    sampling:
      probability: 1.0
    propagation:
      type: b3
  zipkin:
    tracing:
      endpoint: http://localhost:9411/api/v2/spans

spring:
  application:
    name: user-service

2.3 日志集成

yaml
logging:
  pattern:
    level: "%5p [${spring.application.name:},%X{traceId:-},%X{spanId:-}]"

3. 自定义 Span

3.1 手动创建 Span

java
@Service
public class OrderService {
    
    private final Tracer tracer;
    
    public Order createOrder(OrderRequest request) {
        Span span = tracer.nextSpan().name("create-order");
        
        try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) {
            span.tag("order.type", request.getType());
            span.tag("user.id", request.getUserId().toString());
            span.event("order.started");
            
            Order order = doCreateOrder(request);
            
            span.tag("order.id", order.getId().toString());
            span.event("order.completed");
            
            return order;
        } catch (Exception e) {
            span.event("order.failed");
            span.tag("error", e.getMessage());
            throw e;
        } finally {
            span.end();
        }
    }
}

3.2 使用注解

java
@Service
public class PaymentService {
    
    @NewSpan("process-payment")
    public Payment processPayment(Long orderId, BigDecimal amount) {
        // 自动创建 Span
        return doProcessPayment(orderId, amount);
    }
    
    @NewSpan
    @SpanTag(key = "order.id", expression = "#orderId")
    public Payment getPayment(@SpanTag("payment.source") String source, Long orderId) {
        return paymentRepository.findByOrderId(orderId);
    }
}

3.3 添加 Tag 和 Event

java
@Service
public class UserService {
    
    private final Tracer tracer;
    
    public User getUser(Long id) {
        Span span = tracer.currentSpan();
        
        if (span != null) {
            span.tag("user.id", id.toString());
            span.event("db.query.started");
        }
        
        User user = userRepository.findById(id).orElse(null);
        
        if (span != null) {
            span.event("db.query.completed");
            if (user != null) {
                span.tag("user.name", user.getUsername());
            }
        }
        
        return user;
    }
}

4. 跨服务追踪

4.1 Feign 集成

java
@Configuration
public class FeignTracingConfig {
    
    @Bean
    public RequestInterceptor tracingInterceptor(Tracer tracer) {
        return template -> {
            Span currentSpan = tracer.currentSpan();
            if (currentSpan != null) {
                String traceId = currentSpan.context().traceId();
                String spanId = currentSpan.context().spanId();
                
                template.header("X-B3-TraceId", traceId);
                template.header("X-B3-SpanId", spanId);
                template.header("X-B3-Sampled", "1");
            }
        };
    }
}

4.2 RestTemplate 集成

java
@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate(Tracer tracer) {
        RestTemplate restTemplate = new RestTemplate();
        
        restTemplate.getInterceptors().add((request, body, execution) -> {
            Span currentSpan = tracer.currentSpan();
            if (currentSpan != null) {
                request.getHeaders().add("X-B3-TraceId", currentSpan.context().traceId());
                request.getHeaders().add("X-B3-SpanId", currentSpan.context().spanId());
            }
            return execution.execute(request, body);
        });
        
        return restTemplate;
    }
}

4.3 WebClient 集成

java
@Configuration
public class WebClientConfig {
    
    @Bean
    public WebClient webClient(Tracer tracer) {
        return WebClient.builder()
            .filter((request, next) -> {
                Span currentSpan = tracer.currentSpan();
                if (currentSpan != null) {
                    request = ClientRequest.from(request)
                        .header("X-B3-TraceId", currentSpan.context().traceId())
                        .header("X-B3-SpanId", currentSpan.context().spanId())
                        .build();
                }
                return next.exchange(request);
            })
            .build();
    }
}

5. Zipkin 集成

5.1 Zipkin 配置

yaml
spring:
  zipkin:
    base-url: http://localhost:9411
    sender:
      type: web
    compression:
      enabled: true

management:
  tracing:
    sampling:
      probability: 1.0

5.2 自定义 Reporter

java
@Configuration
public class ZipkinConfig {
    
    @Bean
    public Reporter<Span> spanReporter() {
        return AsyncReporter.create(
            URLConnectionSender.create("http://localhost:9411/api/v2/spans")
        );
    }
    
    @Bean
    public Tracing tracing(Reporter<Span> spanReporter) {
        return Tracing.newBuilder()
            .localServiceName("user-service")
            .spanReporter(spanReporter)
            .propagationFactory(B3Propagation.FACTORY)
            .build();
    }
}

6. Jaeger 集成

6.1 添加依赖

xml
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>

6.2 Jaeger 配置

yaml
management:
  tracing:
    enabled: true
    sampling:
      probability: 1.0
  otlp:
    tracing:
      endpoint: http://localhost:4317

spring:
  application:
    name: user-service

7. 追踪数据导出

7.1 自定义导出

java
@Configuration
public class TracingExportConfig {
    
    @Bean
    public SpanHandler spanHandler() {
        return new SpanHandler() {
            @Override
            public boolean end(TraceContext context, MutableSpan span, Cause cause) {
                // 自定义处理 Span 数据
                log.info("Span: {} - Duration: {}ms", 
                    span.name(), 
                    span.duration());
                
                // 发送到自定义存储
                spanStorage.save(span);
                
                return true;
            }
        };
    }
}

7.2 追踪数据存储

java
@Service
public class SpanStorageService {
    
    private final ElasticsearchClient esClient;
    
    public void save(MutableSpan span) {
        SpanDocument doc = new SpanDocument();
        doc.setTraceId(span.traceId());
        doc.setSpanId(span.spanId());
        doc.setName(span.name());
        doc.setDuration(span.duration());
        doc.setTags(span.tags());
        doc.setTimestamp(Instant.ofEpochMilli(span.startTimestamp()));
        
        esClient.index(i -> i
            .index("spans")
            .id(doc.getTraceId() + "-" + doc.getSpanId())
            .document(doc)
        );
    }
}

8. 追踪可视化

8.1 自定义追踪端点

java
@RestController
@RequestMapping("/trace")
public class TraceController {
    
    private final Tracer tracer;
    
    @GetMapping("/current")
    public Map<String, Object> getCurrentTrace() {
        Span currentSpan = tracer.currentSpan();
        
        if (currentSpan == null) {
            return Map.of("message", "No active trace");
        }
        
        return Map.of(
            "traceId", currentSpan.context().traceId(),
            "spanId", currentSpan.context().spanId(),
            "name", currentSpan.name(),
            "tags", currentSpan.tags()
        );
    }
}

9. 小结

本章学习了 Spring Cloud 链路追踪的核心内容:

内容要点
核心概念Trace、Span、TraceId
Micrometer Tracing配置、集成
自定义 Span手动创建、注解方式
跨服务追踪Feign、RestTemplate、WebClient
Zipkin配置、Reporter
JaegerOpenTelemetry 集成
数据导出自定义存储

下一章将学习 Spring Cloud Kubernetes。