Java链路追踪案例

wen java案例 1

本文目录导读:

Java链路追踪案例

  1. 项目结构
  2. 父POM配置文件
  3. 服务A的实现
  4. 服务B的实现
  5. 服务C的实现
  6. 配置文件
  7. 运行Docker Zipkin
  8. 启动和测试
  9. 查看链路追踪
  10. 日志输出示例
  11. 自定义链路追踪(高级用法)
  12. 使用Spring Cloud Gateway集成Zipkin
  13. 注意事项

我将为您提供一个完整的Java链路追踪案例,使用Spring Cloud Sleuth和Zipkin实现分布式系统的链路追踪。

项目结构

tracing-demo/
├── pom.xml (父POM)
├── service-a/          # 服务A
├── service-b/          # 服务B
└── service-c/          # 服务C

父POM配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>tracing-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath/>
    </parent>
    <properties>
        <java.version>11</java.version>
        <spring-cloud.version>2021.0.8</spring-cloud.version>
    </properties>
    <modules>
        <module>service-a</module>
        <module>service-b</module>
        <module>service-c</module>
    </modules>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

服务A的实现

ServiceAApplication.java

package com.example.servicea;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@RestController
public class ServiceAApplication {
    private static final Logger logger = LoggerFactory.getLogger(ServiceAApplication.class);
    @Autowired
    private RestTemplate restTemplate;
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    @GetMapping("/api/service-a")
    public String serviceA() {
        logger.info("Service A received request");
        // 模拟业务处理
        String result = "Service A processed. ";
        // 调用Service B
        logger.info("Service A calling Service B");
        String serviceBResponse = restTemplate.getForObject(
            "http://localhost:8081/api/service-b", String.class);
        result += serviceBResponse;
        logger.info("Service A completed request");
        return result;
    }
    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
    }
}

服务B的实现

ServiceBApplication.java

package com.example.serviceb;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@RestController
public class ServiceBApplication {
    private static final Logger logger = LoggerFactory.getLogger(ServiceBApplication.class);
    @Autowired
    private RestTemplate restTemplate;
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    @GetMapping("/api/service-b")
    public String serviceB() throws InterruptedException {
        logger.info("Service B received request");
        // 模拟耗时操作
        Thread.sleep(100);
        // 调用Service C
        logger.info("Service B calling Service C");
        String serviceCResponse = restTemplate.getForObject(
            "http://localhost:8082/api/service-c", String.class);
        logger.info("Service B completed request");
        return "Service B processed. " + serviceCResponse;
    }
    public static void main(String[] args) {
        SpringApplication.run(ServiceBApplication.class, args);
    }
}

服务C的实现

ServiceCApplication.java

package com.example.servicec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@RestController
public class ServiceCApplication {
    private static final Logger logger = LoggerFactory.getLogger(ServiceCApplication.class);
    @GetMapping("/api/service-c")
    public String serviceC() throws InterruptedException {
        logger.info("Service C received request");
        // 模拟耗时操作
        Thread.sleep(200);
        logger.info("Service C completed request");
        return "Service C processed";
    }
    public static void main(String[] args) {
        SpringApplication.run(ServiceCApplication.class, args);
    }
}

配置文件

service-a/src/main/resources/application.yml

server:
  port: 8080
spring:
  application:
    name: service-a
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1.0  # 采样率,1.0表示100%采样
logging:
  level:
    org.springframework.web: INFO
    com.example: DEBUG

service-b/src/main/resources/application.yml

server:
  port: 8081
spring:
  application:
    name: service-b
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1.0
logging:
  level:
    org.springframework.web: INFO
    com.example: DEBUG

service-c/src/main/resources/application.yml

server:
  port: 8082
spring:
  application:
    name: service-c
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1.0
logging:
  level:
    org.springframework.web: INFO
    com.example: DEBUG

运行Docker Zipkin

# 运行Zipkin服务
docker run -d -p 9411:9411 openzipkin/zipkin

启动和测试

启动三个服务

# 启动Service A
mvn spring-boot:run -pl service-a
# 启动Service B  
mvn spring-boot:run -pl service-b
# 启动Service C
mvn spring-boot:run -pl service-c

测试请求

curl http://localhost:8080/api/service-a

预期输出

Service A processed. Service B processed. Service C processed

查看链路追踪

  1. 访问Zipkin UI: http://localhost:9411
  2. 点击"Find Traces"查看所有链路
  3. 可以看到完整的调用链路:Service A → Service B → Service C

日志输出示例

2024-01-15 10:30:45.123  [service-a,3f4a5b6c7d8e9f0a,3f4a5b6c7d8e9f0a,true] 
INFO com.example.servicea.ServiceAApplication - Service A received request
2024-01-15 10:30:45.200  [service-b,3f4a5b6c7d8e9f0a,2e8f0f1a2b3c4d5e,true] 
INFO com.example.serviceb.ServiceBApplication - Service B received request
2024-01-15 10:30:45.300  [service-c,3f4a5b6c7d8e9f0a,5a6b7c8d9e0f1a2b,true] 
INFO com.example.servicec.ServiceCApplication - Service C received request

其中日志格式为:[应用名, TraceId, SpanId, 是否导出]

自定义链路追踪(高级用法)

自定义Span创建

package com.example.servicea;
import brave.Span;
import brave.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CustomTracingController {
    @Autowired
    private Tracer tracer;
    @GetMapping("/custom-trace")
    public String customTrace() {
        // 创建自定义Span
        Span customSpan = tracer.nextSpan().name("custom-operation").start();
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(customSpan)) {
            // 添加自定义标签
            customSpan.tag("custom.tag", "custom-value");
            customSpan.annotate("custom-annotation");
            // 业务逻辑
            Thread.sleep(500);
            return "Custom tracing completed";
        } catch (Exception e) {
            customSpan.error(e);
            return "Error occurred";
        } finally {
            customSpan.finish();
        }
    }
}

使用Spring Cloud Gateway集成Zipkin

Gateway配置

spring:
  application:
    name: api-gateway
  zipkin:
    base-url: http://localhost:9411
  sleuth:
    sampler:
      probability: 1.0
  cloud:
    gateway:
      routes:
        - id: service-a
          uri: http://localhost:8080
          predicates:
            - Path=/api/service-a/**

注意事项

  1. 采样率设置:生产环境建议设置为小于1的值(如0.1),减少性能损耗
  2. 异步处理:使用@Async或消息队列时,需要额外配置以保证链路追踪的传递
  3. 多线程传播:使用ExecutorService时,需要手动传播trace context
  4. 数据库调用:可以通过集成spring-cloud-sleuth-zipkin的JDBC拦截器来追踪数据库操作
  5. 生产环境:考虑使用ELK等日志系统收集日志,配合Zipkin实现完整的监控体系

这个案例展示了完整的分布式链路追踪实现,可以帮助您快速定位分布式系统中的性能瓶颈和故障点。

上一篇Java TTL案例

下一篇MDC案例

抱歉,评论功能暂时关闭!