链路追踪在微服务中的核心作用
微服务架构下,一个用户请求可能跨越 5-10 个服务,每个服务内部还有数据库、缓存、消息队列等多层调用。没有链路追踪,排查一次 P5 级别的延迟问题可能需要 3 个人花半天时间逐个服务翻日志。链路追踪让每次请求的完整调用链可视化,定位性能瓶颈和错误根因的效率提升一个数量级。
Spring Cloud Sleuth 在 3.x 后已停止维护,官方推荐迁移到 OpenTelemetry。OpenTelemetry 是 CNCF 的可观测性标准,统一了 traces、metrics、logs 三大信号,且与厂商解耦,数据可导出到 Jaeger、Zipkin、Tempo 等任意后端。
OpenTelemetry Agent 接入方式
Java 应用接入 OpenTelemetry 最简单的方式是使用 Agent 自动埋点,无需修改业务代码:
<!-- pom.xml 添加依赖 -->
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
<version>2.10.0-alpha</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
应用配置:
# application.yml
otel:
exporter:
otlp:
endpoint: http://otel-collector:4317
protocol: grpc
resource:
attributes:
service.name: order-service
service.version: 2.1.0
deployment.environment: production
traces:
sampler:
type: parentbased_traceidratio
arg: 0.1 # 生产环境采样 10%
instrumentation:
spring-scheduling:
enabled: true
jdbc:
enabled: true
redis:
enabled: true
kafka:
enabled: true
如果希望零侵入,使用 Java Agent 方式启动:
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.service.name=order-service \
-Dotel.exporter.otlp.endpoint=http://otel-collector:4317 \
-Dotel.traces.sampler=parentbased_traceidratio \
-Dotel.traces.sampler.arg=0.1 \
-jar app.jar
手动埋点与 Span 属性
自动埋点覆盖了 HTTP、RPC、数据库等标准框架,但业务关键节点的手动埋点同样重要:
@Service
@RequiredArgsConstructor
public class OrderService {
private final Tracer tracer;
public OrderResult createOrder(OrderRequest request) {
// 创建子 Span 记录业务操作
Span span = tracer.spanBuilder("order.create")
.setAttribute("order.type", request.getType())
.setAttribute("customer.id", request.getCustomerId())
.setAttribute("order.items.count", request.getItems().size())
.startSpan();
try (Scope scope = span.makeCurrent()) {
// 业务逻辑
OrderResult result = processOrder(request);
span.setAttribute("order.id", result.getOrderId());
span.setAttribute("order.amount", result.getTotalAmount());
return result;
} catch (Exception e) {
span.recordException(e);
span.setStatus(StatusCode.ERROR, e.getMessage());
throw e;
} finally {
span.end();
}
}
}
Span 属性是排查问题的关键线索。建议在以下位置添加属性:
- 服务入口:请求来源、用户 ID、设备信息
- 数据库操作:SQL 类型、影响行数、慢查询标记
- 外部调用:目标服务名、响应状态码、耗时
- 业务关键节点:订单号、金额、状态变更
OpenTelemetry Collector 部署架构
Collector 是数据的中间层,负责接收、处理、导出遥测数据。推荐部署模式:
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
# 限流保护 Collector 自身
rate_limiting:
spans_per_second: 5000
# 批量导出减少网络开销
batch:
send_batch_size: 1024
timeout: 5s
# 资源属性标准化
resource:
attributes:
- key: env
value: production
action: upsert
# 过滤掉健康检查的噪音 Span
filter:
error_mode: ignore
traces:
span:
- 'http.route == "/health"'
- 'http.route == "/ready"'
exporters:
# 导出到 Jaeger
otlp/jaeger:
endpoint: jaeger:4317
tls:
insecure: true
# 导出到 Prometheus(Metrics)
prometheus:
endpoint: 0.0.0.0:8889
# 导出到日志文件
file:
path: /var/log/otel/traces.json
service:
pipelines:
traces:
receivers: [otlp]
processors: [filter, rate_limiting, batch]
exporters: [otlp/jaeger]
metrics:
receivers: [otlp]
processors: [resource, batch]
exporters: [prometheus]
采样策略与性能平衡
全量采集 Trace 的代价很高。一个日均千万级请求的系统,全量采集会产生数亿 Span,存储成本和查询性能都是问题。采样策略需要在可观测性和成本间找平衡:
# 基于规则的采样:错误请求全量采集,正常请求采样
# 在 Collector 中配置 tail_sampling
processors:
tail_sampling:
decision_wait: 10s
num_traces: 100000
expected_new_traces_per_sec: 1000
policies:
# 错误请求全量采集
- name: error-policy
type: status_code
status_code:
status_codes:
- ERROR
# 慢请求全量采集(>2s)
- name: slow-policy
type: latency
latency:
threshold_ms: 2000
# 其他请求采样 5%
- name: baseline-policy
type: trace_id_ratio
trace_id_ratio:
sampling_percentage: 5
从 Sleuth 迁移注意事项
如果项目已经使用 Sleuth + Zipkin,迁移到 OpenTelemetry 需要处理几个兼容性问题:
1. Trace ID 格式变化
Sleuth 生成的 Trace ID 是 64 位十六进制(16 字符),OpenTelemetry 使用 128 位(32 字符)。跨系统调用时,ID 格式不匹配会导致链路断裂。解决方案是在 Collector 中做 ID 兼容处理,或统一所有服务迁移到 OpenTelemetry。
2. Baggage 传播
Sleuth 的 Baggage 通过 HTTP Header baggage 传播。OpenTelemetry 使用 W3C Baggage 规范,Header 名称一致但格式有细微差别。需要确认下游服务是否支持 W3C Baggage。
// 手动传播 Baggage
try (Scope scope = Baggage.builder()
.put("user.id", userId)
.put("tenant.id", tenantId)
.build()
.makeCurrent()) {
// 此范围内的所有 Span 都能读取 Baggage
downstreamClient.call(request);
}
3. 日志关联
Sleuth 自动在日志中注入 traceId 和 spanId。迁移到 OpenTelemetry 后,需要配置 Logback 或 Log4j2 的 MDC 注入:
<!-- logback-spring.xml -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%X{traceId},%X{spanId}] %-5level %logger - %msg%n</pattern>
OpenTelemetry Agent 自动将 traceId 和 spanId 注入 MDC,无需额外配置。但需确认日志采集链路(Filebeat/Fluentd)能正确解析 MDC 字段,并在日志平台(如 ELK)中支持按 traceId 检索。
链路追踪是微服务可观测性的基石,从 Sleuth 迁移到 OpenTelemetry 虽然有一定工作量,但 OpenTelemetry 的统一标准和丰富生态是长期收益。建议分批迁移:先在新服务上使用 OpenTelemetry,老服务按迭代计划逐步切换,中间通过 Collector 做数据聚合。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/springboot-wei-fu-wu-lian-lu-zhui-zong-shi-zhan-cong-sleuth/