Spring Cloud 链路追踪
1. 链路追踪概述
1.1 为什么需要链路追踪
- 故障定位:快速定位问题所在
- 性能分析:分析调用链性能瓶颈
- 依赖分析:了解服务间依赖关系
- 调用可视化:直观展示调用链路
1.2 核心概念
| 概念 | 说明 |
|---|---|
| Trace | 一次完整请求的追踪链路 |
| Span | 追踪链路中的一个节点 |
| Annotation | Span 中的事件标记 |
| TraceId | 全局唯一的追踪 ID |
| SpanId | Span 的唯一标识 |
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-service2.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.05.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-service7. 追踪数据导出
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 |
| Jaeger | OpenTelemetry 集成 |
| 数据导出 | 自定义存储 |
下一章将学习 Spring Cloud Kubernetes。