第 229 章:链路追踪
学习目标
- 理解分布式调用链
- 掌握 Sleuth 集成
- 学会 Zipkin / SkyWalking
- 分析性能瓶颈
一、为什么需要链路追踪
问题:用户投诉"下单慢",但不知道哪一步慢。
链路追踪:能可视化每次调用的耗时、节点、状态。
二、核心概念
2.1 Trace + Span
| 概念 | 说明 |
|---|---|
| Trace | 一次完整调用链(从入口到所有下游) |
| Span | 一个调用节点(单次方法调用) |
| traceId | 全局唯一,标识一次请求 |
| spanId | 单 span 的 ID |
| parentId | 父 span 的 ID |
2.2 上下文传播
通过 HTTP Header 透传:X-B3-TraceId、X-B3-SpanId、X-B3-ParentSpanId。
三、Sleuth + Zipkin
3.1 Sleuth 自动埋点
Spring Cloud Sleuth 自动在以下框架埋点:
- HTTP (RestTemplate, WebClient, OpenFeign)
- MQ (Spring AMQP, Spring Kafka)
- 调度任务(Scheduled)
- 异步 (@Async)
3.2 依赖
xml
<!-- 微服务 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>3.3 配置
yaml
spring:
application:
name: order-service
zipkin:
base-url: http://zipkin-server:9411
sender:
type: web
sleuth:
sampler:
probability: 1.0 # 全部采样(开发)
propagation:
type: B3 # Zipkin B3
trace:
enabled: true3.4 启动 Zipkin
bash
docker run -d -p 9411:9411 \
-e STORAGE_TYPE=elasticsearch \
openzipkin/zipkin或简化版(内存存储,重启丢失):
bash
docker run -d -p 9411:9411 openzipkin/zipkin访问:http://localhost:9411
3.5 自定义埋点
java
@Service
@RequiredArgsConstructor
public class OrderService {
// 通过 Sleuth 自动埋点
private final Tracer tracer;
public Order create(OrderRequest req) {
// 业务前打开新 span
Span newSpan = tracer.nextSpan().name("customProcess");
try (Tracer.SpanInScope ws = tracer.withSpan(newSpan.start())) {
// 自定义业务逻辑
return process(req);
} finally {
newSpan.end();
}
}
}或注解方式:
java
@NewSpan("customLogic")
public void process(OrderRequest req) {
// 业务
}
@SpanTag("skuId")
public void deduct(String skuId) {
// 自动打 tag
}四、SkyWalking
4.1 简介
Apache SkyWalking 是国产开源的 APM,功能比 Sleuth+Zipkin 更强。
4.2 服务端部署
yaml
# docker-compose.yml
services:
skywalking-oap:
image: apache/skywalking-oap-server:9.7.0
ports: ["11800:11800", "12800:12800"]
environment:
SW_STORAGE: elasticsearch
SW_STORAGE_ES_CLUSTER_NODES: elasticsearch:9200
skywalking-ui:
image: apache/skywalking-ui:9.7.0
ports: ["8080:8080"]
depends_on:
- skywalking-oap
elasticsearch:
image: elasticsearch:7.17.10
environment:
discovery.type: single-node
ports: ["9200:9200"]4.3 客户端(Java Agent)
无需改代码,在启动命令加探针:
bash
java -javaagent:/opt/skywalking/agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=127.0.0.1:11800 \
-jar order-service.jarK8s 中通过 sidecar 注入。
4.4 关键能力
服务拓扑
链路追踪
每个请求一行展示,鼠标悬停看详情。
慢 SQL
sql
SELECT * FROM big_table
WHERE create_at > ?
AND status = 'ACTIVE'SkyWalking 自动捕获执行时间。
告警
3 分钟内某接口 P99 > 500ms
5 分钟内某服务错误率 > 5%配置 webhook 通知。
4.5 OpenTelemetry(下一代标准)
xml
<dependency>
<groupId>io.opentelemetry.javaagent</groupId>
<artifactId>opentelemetry-javaagent</artifactId>
</dependency>bash
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.exporter.otlp.endpoint=http://collector:4317 \
-jar app.jar支持多后端:Jaeger / Tempo / Zipkin 等。
五、追踪与日志结合
5.1 日志中带 traceId
xml
<!-- logstash-logback 依赖 -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
</dependency>logback-spring.xml:
xml
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeMdcKeyName>traceId</includeMdcKeyName>
<includeMdcKeyName>spanId</includeMdcKeyName>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>输出:
json
{
"timestamp": "2026-08-13T10:30:00.000+08:00",
"level": "INFO",
"message": "创建订单",
"traceId": "abc123",
"spanId": "1",
"service": "order-service"
}5.2 在 Kibana/Grafana Loki 通过 traceId 查询
traceId:abc1235.3 反向跳转
Trace UI → 点击 span → 直接跳转到日志平台查同一 traceId。
六、性能分析
6.1 P99 定位
6.2 服务地图
一眼看出库存是瓶颈。
6.3 调用频率
GET /api/orders QPS 1000 P99 80ms ✅
GET /api/order/{id} QPS 50 P99 200ms ⚠️七、自研埋点(Brave)
不依赖 Spring Cloud Sleuth,直接用 Zipkin Brave。
xml
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>java
@Configuration
public class TraceConfig {
@Bean
public Brave brave() {
Sender sender = OkHttpSender.create("http://zipkin:9411/api/v2/spans");
AsyncReporter reporter = AsyncReporter.create(sender);
Brave brave = new Brave.Builder("order-service")
.reporter(reporter)
.traceSampler(Sampler.ALWAYS_SAMPLE)
.build();
return brave;
}
}八、采样策略
8.1 全量 vs 概率
yaml
spring:
sleuth:
sampler:
probability: 0.1 # 10% 采样8.2 尾部采样(高级)
只在追踪结束时决定是否上报。
yaml
# Zipkin 用 Brave 时启用
zipkin:
async:
enabled: true8.3 强制采样(关键路径)
java
@Sampled
public void criticalRequest() {
// 强制采样
}九、与 OpenTelemetry 对接
9.1 当前格局
- Sleuth → Zipkin / 隐退
- OpenTelemetry → 后起之秀,统一标准
- SkyWalking → 自有体系
9.2 OTel 优势
- CNCF 主导
- 多语言支持
- 与厂商无关
9.3 迁移建议
xml
<!-- 兼容方案:Sleuth + OTel Bridge -->
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>十、本章小结
| 工具 | 特点 |
|---|---|
| Sleuth + Zipkin | Spring 全家桶,轻量 |
| SkyWalking | 国产,功能强 |
| OpenTelemetry | 标准,云原生 |
| 自研 Brave | 灵活,代码可控 |
动手练习
- 部署 Zipkin,Spring Boot 自动埋点,启动两个服务互相调用
- 自定义
@NewSpan标记业务方法 - 在日志中输出 traceId,通过日志平台查询
- 用 SkyWalking Agent 接 Java 应用,看服务拓扑图
推荐阅读
下一章:第 230 章:微服务安全