调用链追踪(Trace ID)
前言:
在 Java 中实现 调用链追踪(Trace ID) 通常用于分布式系统中跟踪请求的完整链路,常见的实现方式包括手动编码或使用开源框架(如 SkyWalking
、Zipkin
、Spring Cloud Sleuth
等)。以下是具体实现方法及示例:
1. 手动实现 Trace ID
通过 ThreadLocal
或 MDC
(Mapped Diagnostic Context)存储 Trace ID,并在请求链路中传递。
步骤 1:定义 Trace ID 工具类
import org.slf4j.MDC;
import java.util.UUID;
public class TraceIdUtil {
private static final String TRACE_ID_KEY = "traceId";
private static final ThreadLocal<String> traceIdHolder = new ThreadLocal<>();
// 生成或获取当前 Trace ID
public static String getOrGenerateTraceId() {
String traceId = MDC.get(TRACE_ID_KEY);
if (traceId == null) {
traceId = UUID.randomUUID().toString().replace("-", "");
MDC.put(TRACE_ID_KEY, traceId);
traceIdHolder.set(traceId);
}
return traceId;
}
// 清除 Trace ID(防止内存泄漏)
public static void clear() {
MDC.remove(TRACE_ID_KEY);
traceIdHolder.remove();
}
}
步骤 2:在 HTTP 请求中传递 Trace ID
通过拦截器在请求头中添加 Trace ID:
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
public class TraceIdInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution
) throws IOException {
String traceId = TraceIdUtil.getOrGenerateTraceId();
request.getHeaders().add("X-Trace-Id", traceId);
return execution.execute(request, body);
}
}
步骤 3:在日志中输出 Trace ID
配置 logback.xml
,在日志模板中添加 %X{traceId}
:
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
2. 使用 Spring Cloud Sleuth + Zipkin(推荐)
Spring Cloud Sleuth 自动集成 Trace ID,Zipkin 提供可视化追踪。
步骤 1:添加依赖
<!-- Spring Cloud Sleuth -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- Zipkin 上报 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
步骤 2:配置 application.yml
spring:
sleuth:
sampler:
probability: 1.0 # 采样率(1.0=100%上报)
zipkin:
base-url: http://localhost:9411 # Zipkin 服务地址
步骤 3:查看调用链
启动 Zipkin 服务后,访问 http://localhost:9411
查看完整的调用链路。
3. 使用 SkyWalking
SkyWalking 是一款开源的 APM 系统,支持自动链路追踪。
步骤 1:下载并启动 SkyWalking
# 下载 SkyWalking
wget https://archive.apache.org/dist/skywalking/9.6.0/apache-skywalking-apm-9.6.0.tar.gz
tar -zxvf apache-skywalking-apm-9.6.0.tar.gz
cd apache-skywalking-apm-bin
# 启动 SkyWalking
bin/startup.sh
步骤 2:Java 应用接入 Agent
在 JVM 启动参数中添加 Agent:
-javaagent:/path/to/skywalking-agent/skywalking-agent.jar
-Dskywalking.agent.service_name=your-service-name
-Dskywalking.collector.backend_service=localhost:11800
4. 异步线程传递 Trace ID
使用 TransmittableThreadLocal
解决线程池中 Trace ID 丢失问题:
import com.alibaba.ttl.TransmittableThreadLocal;
public class TraceContext {
private static final TransmittableThreadLocal<String> traceIdHolder =
new TransmittableThreadLocal<>();
public static void setTraceId(String traceId) {
traceIdHolder.set(traceId);
}
public static String getTraceId() {
return traceIdHolder.get();
}
public static void clear() {
traceIdHolder.remove();
}
}
// 在线程池任务中调用
ExecutorService executor = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(10));
executor.submit(() -> {
System.out.println("Trace ID: " + TraceContext.getTraceId());
});
关键点总结
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
手动实现 Trace ID | 简单项目或框架不支持链路追踪时 | 轻量级,无依赖 | 需自行处理传递逻辑 |
Spring Cloud Sleuth | Spring Cloud 微服务项目 | 自动化集成,支持 Zipkin 可视化 | 依赖 Spring 生态 |
SkyWalking | 企业级监控,多语言支持 | 功能强大,支持全链路追踪 | 需部署额外服务 |
根据项目需求选择合适方案!