🔍 服务调用链追踪:微服务的"GPS定位系统"!

46 阅读12分钟

副标题:Zipkin、Skywalking、Jaeger三剑客,谁能拯救你的调用链迷宫?🗺️


🎬 开场:一个令人头疼的故障

凌晨3点,张工被电话吵醒 ☎️:

老板:用户反馈下单很慢,赶紧查!
张工:好的!(打开监控系统)

系统架构:
用户 → 网关 → 订单服务 → 商品服务 → 库存服务 → 支付服务
                    ↓         ↓          ↓          ↓
                  用户服务  优惠服务   积分服务   通知服务

张工:😱 10个微服务,到底哪个慢了?
     每个服务的日志都在不同的机器上
     请求ID也不统一
     完全不知道从哪里查起...

结果:排查了2小时才定位到是优惠券服务数据库连接池满了

张工:😭 要是有个东西能看到整个调用链路就好了...

这就是我们今天要讲的:分布式链路追踪!


📚 什么是链路追踪?

官方定义

分布式链路追踪(Distributed Tracing):在分布式系统中,追踪一个请求从进入系统到返回的完整路径,记录经过的所有服务节点、调用关系、耗时等信息。

通俗解释

就像快递追踪 📦:

你在淘宝下单买了一本书

传统监控(只能看到某个环节):
- 仓库:✅ 已发货
- 物流:❓ 不知道
- 配送:❓ 不知道

链路追踪(看到完整路径):
14:00  [北京仓库] 已发货
14:30  [北京分拨中心] 已到达
15:00  [北京分拨中心] 已发出
18:00  [上海转运中心] 已到达  ⚠️ 等了3小时!
18:10  [上海转运中心] 已发出
20:00  [上海配送站] 派送中
21:00  [你家门口] 已签收

一眼就能看出:上海转运中心效率低!

🔑 核心概念

1️⃣ Trace(追踪)

一次完整的请求链路

Trace ID: 550e8400-e29b-41d4-a716-446655440000

用户下单请求 → 经过多个服务 → 返回结果
    ↑                              ↓
    └──────────── 一个Trace ───────┘

生活比喻 🎬:

  • Trace = 一部完整的电影
  • 你从买票进场到散场离开的整个过程

2️⃣ Span(跨度)

一个服务内的操作单元

Trace
  └── Span (订单服务)
       ├── Span (查询用户)
       ├── Span (查询商品)
       ├── Span (扣减库存)
       └── Span (创建订单)

Span的属性

public class Span {
    private String traceId;        // 追踪ID
    private String spanId;         // 跨度ID
    private String parentSpanId;   // 父Span ID
    private String serviceName;    // 服务名
    private String operationName;  // 操作名
    private long startTime;        // 开始时间
    private long duration;         // 持续时间
    private Map<String, String> tags;  // 标签
    private List<Log> logs;        // 日志
}

生活比喻 🎬:

  • Span = 电影中的一个场景
  • 每个场景有开始时间、结束时间、演员、台词等

3️⃣ Annotation(注解)

Span中的关键事件

Span生命周期:
CS (Client Send)      → 客户端发送请求
SR (Server Receive)   → 服务端接收请求
SS (Server Send)      → 服务端返回响应
CR (Client Receive)   → 客户端接收响应

时间线:
CS ───────────→ SR ────处理──── SS ───────→ CR
│              │                │          │
└─ 网络耗时 ─┘                  └─ 网络耗时 ─┘
               └──── 服务处理时间 ────┘

可视化理解

Trace: 用户下单 (TraceId: abc123)

├── Span1: 订单服务 (100ms)
   
   ├── Span2: 查询用户服务 (20ms)
   
   ├── Span3: 查询商品服务 (30ms)
      
      └── Span4: 查询数据库 (25ms)  ⚠️ 慢!
   
   └── Span5: 扣减库存服务 (40ms)

└── Span6: 发送MQ消息 (10ms)

总耗时:100ms
瓶颈:Span4查询数据库慢

🏗️ 链路追踪的实现原理

核心机制:上下文传播

服务A                    服务B                    服务C
                                                  
   生成TraceId                                    
   TraceId: T1                                    
   SpanId: S1                                     
                                                  
  ├─── HTTP请求 ─────────→                         
      Header:                                     
      X-B3-TraceId: T1     解析Header            
      X-B3-SpanId: S2      TraceId: T1           
      X-B3-ParentSpanId:S1│ SpanId: S2            
                                                  
                          ├─── HTTP请求 ─────────→ 
                              Header:             
                              X-B3-TraceId: T1    
                              X-B3-SpanId: S3     
                              X-B3-ParentSpanId:S2│
                                                  

关键点

  1. 生成TraceId:第一个服务生成全局唯一的TraceId
  2. 传递上下文:通过HTTP Header传递TraceId、SpanId
  3. 记录Span:每个服务记录自己的Span信息
  4. 上报数据:异步上报到追踪系统

代码示例:手动实现

// 1. 请求入口:生成或提取TraceId
@Component
public class TracingFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        
        // 尝试从Header中获取TraceId
        String traceId = httpRequest.getHeader("X-B3-TraceId");
        String spanId = httpRequest.getHeader("X-B3-SpanId");
        String parentSpanId = httpRequest.getHeader("X-B3-ParentSpanId");
        
        // 如果没有TraceId,说明是链路起点,生成新的
        if (traceId == null) {
            traceId = generateTraceId();
            spanId = generateSpanId();
            parentSpanId = null;
        } else {
            // 如果有TraceId,生成新的SpanId
            parentSpanId = spanId;
            spanId = generateSpanId();
        }
        
        // 存入ThreadLocal
        TraceContext.setTraceId(traceId);
        TraceContext.setSpanId(spanId);
        TraceContext.setParentSpanId(parentSpanId);
        
        // 记录Span开始
        Span span = Span.builder()
            .traceId(traceId)
            .spanId(spanId)
            .parentSpanId(parentSpanId)
            .serviceName("order-service")
            .operationName(httpRequest.getRequestURI())
            .startTime(System.currentTimeMillis())
            .build();
        
        try {
            chain.doFilter(request, response);
        } finally {
            // 记录Span结束
            span.setDuration(System.currentTimeMillis() - span.getStartTime());
            
            // 上报Span
            SpanReporter.report(span);
            
            // 清理ThreadLocal
            TraceContext.clear();
        }
    }
}

// 2. 远程调用:传递TraceId
@Component
public class RestTemplateInterceptor implements ClientHttpRequestInterceptor {
    
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) {
        // 从ThreadLocal获取TraceId
        String traceId = TraceContext.getTraceId();
        String parentSpanId = TraceContext.getSpanId();
        String spanId = generateSpanId();
        
        // 添加到请求头
        request.getHeaders().add("X-B3-TraceId", traceId);
        request.getHeaders().add("X-B3-SpanId", spanId);
        request.getHeaders().add("X-B3-ParentSpanId", parentSpanId);
        
        // 记录调用
        Span span = Span.builder()
            .traceId(traceId)
            .spanId(spanId)
            .parentSpanId(parentSpanId)
            .serviceName("order-service")
            .operationName("HTTP " + request.getMethod() + " " + request.getURI())
            .startTime(System.currentTimeMillis())
            .build();
        
        try {
            ClientHttpResponse response = execution.execute(request, body);
            span.setDuration(System.currentTimeMillis() - span.getStartTime());
            SpanReporter.report(span);
            return response;
        } catch (Exception e) {
            span.setError(true);
            span.addTag("error", e.getMessage());
            SpanReporter.report(span);
            throw e;
        }
    }
}

🥊 三大链路追踪系统对比

Zipkin(Twitter出品)🐦

架构图

        应用程序
            ↓
      ┌─────────┐
      │ Reporter │ ← 数据上报(HTTP/Kafka)
      └────┬────┘
           ↓
    ┌──────────────┐
    │   Collector  │ ← 数据收集器
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │   Storage    │ ← 存储(MySQL/ES/Cassandra)
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │      UI      │ ← Web界面
    └──────────────┘

特点

优点 ✅:

  • 历史悠久,生态成熟
  • 轻量级,部署简单
  • 支持多种语言(Java/Go/Python等)
  • 社区活跃

缺点 ❌:

  • 功能相对简单
  • UI不够美观
  • 不支持告警
  • 性能监控能力弱

快速上手

1. 启动Zipkin服务

# 使用Docker启动
docker run -d -p 9411:9411 openzipkin/zipkin

2. Spring Boot集成

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
spring:
  application:
    name: order-service
  zipkin:
    base-url: http://localhost:9411/  # Zipkin地址
  sleuth:
    sampler:
      probability: 1.0  # 采样率100%(生产环境建议0.1)

3. 查看链路

访问:http://localhost:9411

界面功能:
├── 查找追踪  (Find Traces)
├── 依赖分析  (Dependencies)
└── 服务列表  (Services)

4. 自定义Span

@Service
public class OrderService {
    
    @Autowired
    private Tracer tracer;
    
    public Order createOrder(OrderRequest request) {
        // 创建自定义Span
        Span span = tracer.nextSpan().name("createOrder").start();
        
        try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
            // 添加标签
            span.tag("orderId", request.getOrderId().toString());
            span.tag("userId", request.getUserId().toString());
            
            // 业务逻辑
            Order order = doCreateOrder(request);
            
            // 添加事件
            span.annotate("order.created");
            
            return order;
        } catch (Exception e) {
            span.tag("error", e.getMessage());
            throw e;
        } finally {
            span.finish();
        }
    }
}

SkyWalking(Apache项目)🦅

架构图

        应用程序(探针Agent)
            ↓
    ┌──────────────┐
    │  SkyWalking  │
    │    Agent     │ ← 字节码增强,无侵入
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │     OAP      │ ← 后端分析平台
    │  (Collector) │   (Observability Analysis Platform)
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │   Storage    │ ← 存储(ES/H2)
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │      UI      │ ← Web界面
    └──────────────┘

核心优势:无侵入

Zipkin/Jaeger:
需要在代码中引入依赖 ❌
需要手动埋点 ❌

SkyWalking:
无需修改代码!✅
启动时添加JVM参数即可:
java -javaagent:/path/to/skywalking-agent.jar -jar app.jar

特点

优点 ✅:

  • 无侵入:字节码增强,无需修改代码
  • 功能强大:支持链路、指标、日志、告警
  • UI美观:界面友好,功能丰富
  • 国产化:中文文档完善
  • 性能监控:JVM、数据库、MQ等

缺点 ❌:

  • 架构相对复杂
  • 资源占用较高
  • Agent有性能开销

快速上手

1. 下载SkyWalking

# 下载并解压
wget https://archive.apache.org/dist/skywalking/8.9.1/apache-skywalking-apm-8.9.1.tar.gz
tar -zxvf apache-skywalking-apm-8.9.1.tar.gz

# 目录结构
skywalking/
├── agent/           # Agent探针
├── bin/             # 启动脚本
├── config/          # 配置文件
└── oap-libs/        # OAP服务

2. 启动OAP和UI

# 启动OAP服务
cd bin
./oapService.sh

# 启动UI(另一个终端)
./webappService.sh

# 访问:http://localhost:8080

3. 应用集成Agent

# 方式1:启动参数
java -javaagent:/path/to/skywalking-agent.jar \
     -Dskywalking.agent.service_name=order-service \
     -Dskywalking.collector.backend_service=127.0.0.1:11800 \
     -jar order-service.jar

# 方式2:环境变量
export SW_AGENT_NAME=order-service
export SW_AGENT_COLLECTOR_BACKEND_SERVICES=127.0.0.1:11800
java -javaagent:/path/to/skywalking-agent.jar -jar order-service.jar

4. 自定义监控

@Service
public class OrderService {
    
    /**
     * 使用注解标记
     */
    @Trace  // SkyWalking注解
    @Tags({
        @Tag(key = "param", value = "arg[0]"),
        @Tag(key = "result", value = "returnedObj")
    })
    public Order createOrder(OrderRequest request) {
        return doCreateOrder(request);
    }
    
    /**
     * 手动创建Span
     */
    public void manualTrace() {
        AbstractSpan span = ContextManager.createLocalSpan("custom-operation");
        
        try {
            span.tag("key", "value");
            span.log(System.currentTimeMillis(), "event");
            
            // 业务逻辑
            
        } catch (Exception e) {
            span.errorOccurred();
            span.log(e);
        } finally {
            ContextManager.stopSpan();
        }
    }
}

5. 告警配置

# config/alarm-settings.yml
rules:
  # 服务响应时间告警
  service_resp_time_rule:
    metrics-name: service_resp_time
    op: ">"
    threshold: 1000  # 超过1秒
    period: 10       # 10分钟内
    count: 3         # 触发3次
    message: "服务响应时间过长: {name}"
  
  # 服务错误率告警
  service_percent_rule:
    metrics-name: service_percent
    op: "<"
    threshold: 95    # 成功率低于95%
    period: 10
    count: 2
    message: "服务成功率过低: {name}"

# 告警通道
webhooks:
  - url: http://your-webhook-url

Jaeger(Uber出品)🚗

架构图

        应用程序
            ↓
      ┌─────────┐
      │  Agent  │ ← 本地代理(UDP上报)
      └────┬────┘
           ↓
    ┌──────────────┐
    │  Collector   │ ← 收集器(接收、处理、存储)
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │   Storage    │ ← 存储(Cassandra/ES)
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │    Query     │ ← 查询服务
    └──────┬───────┘
           ↓
    ┌──────────────┐
    │      UI      │ ← Web界面
    └──────────────┘

特点

优点 ✅:

  • CNCF毕业项目,云原生首选
  • 支持OpenTracing标准
  • 性能优异
  • 采样策略灵活
  • 适配性强(支持多种存储)

缺点 ❌:

  • 架构复杂(组件多)
  • 部署成本高
  • 国内资料少

快速上手

1. 启动Jaeger(All-in-One)

# Docker启动(适合开发测试)
docker run -d \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  jaegertracing/all-in-one:latest

# 访问UI:http://localhost:16686

2. Spring Boot集成

<dependency>
    <groupId>io.opentracing.contrib</groupId>
    <artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
</dependency>
opentracing:
  jaeger:
    service-name: order-service
    udp-sender:
      host: localhost
      port: 6831
    probabilistic-sampler:
      sampling-rate: 1.0  # 采样率100%

3. 使用OpenTracing API

@Service
public class OrderService {
    
    @Autowired
    private Tracer tracer;
    
    public Order createOrder(OrderRequest request) {
        // 创建Span
        Span span = tracer.buildSpan("createOrder").start();
        
        try (Scope scope = tracer.scopeManager().activate(span)) {
            // 添加标签
            span.setTag("orderId", request.getOrderId());
            span.setTag("userId", request.getUserId());
            
            // 添加日志
            Map<String, Object> fields = new HashMap<>();
            fields.put("event", "order.validation");
            fields.put("message", "订单校验通过");
            span.log(fields);
            
            // 业务逻辑
            Order order = doCreateOrder(request);
            
            return order;
        } catch (Exception e) {
            Tags.ERROR.set(span, true);
            span.log(Collections.singletonMap("event", "error"));
            throw e;
        } finally {
            span.finish();
        }
    }
}

4. 采样策略

// 概率采样:10%的请求被追踪
SamplerConfiguration samplerConfig = SamplerConfiguration.fromEnv()
    .withType("probabilistic")
    .withParam(0.1);

// 速率限制采样:每秒最多追踪10个请求
SamplerConfiguration samplerConfig = SamplerConfiguration.fromEnv()
    .withType("ratelimiting")
    .withParam(10);

// 自定义采样
SamplerConfiguration samplerConfig = SamplerConfiguration.fromEnv()
    .withType("remote")
    .withManagerHostPort("localhost:5778");

⚔️ 三大系统详细对比

对比表

维度ZipkinSkyWalkingJaeger
侵入性需要SDK⭐⭐⭐⭐⭐ 无侵入(Agent)需要SDK
性能开销中(Agent有开销)
功能丰富度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
UI体验⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
告警功能⭐⭐⭐⭐⭐⭐⭐
社区活跃度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
部署难度⭐⭐⭐ 简单⭐⭐⭐ 中等⭐⭐ 复杂
中文文档⭐⭐⭐⭐⭐⭐⭐⭐⭐
适用场景简单追踪全能型云原生

性能对比

吞吐量测试(Spans/秒):

Zipkin:      50000 ⭐⭐⭐
SkyWalking:  45000 ⭐⭐⭐
Jaeger:      60000 ⭐⭐⭐⭐

存储空间占用(相同数据量):

Zipkin:      1GB  ⭐⭐⭐
SkyWalking:  1.2GB ⭐⭐
Jaeger:      0.8GB ⭐⭐⭐⭐

Agent内存占用:

Zipkin:      无Agent  -
SkyWalking:  ~50MB   ⭐⭐⭐
Jaeger:      无Agent  -

🤔 如何选择?

决策树 🌲

开始选择链路追踪系统
 │
 ├─ 不想修改代码?
 │   └─ 是 → SkyWalking ⭐⭐⭐⭐⭐
 │
 ├─ 需要告警功能?
 │   └─ 是 → SkyWalking ⭐⭐⭐⭐⭐
 │
 ├─ 云原生(K8s)环境?
 │   └─ 是 → Jaeger ⭐⭐⭐⭐⭐
 │
 ├─ 快速上手,简单场景?
 │   └─ 是 → Zipkin ⭐⭐⭐⭐
 │
 ├─ 需要全面的APM功能?
 │   └─ 是 → SkyWalking ⭐⭐⭐⭐⭐
 │
 └─ 团队熟悉OpenTracing?
     └─ 是 → Jaeger ⭐⭐⭐⭐

典型场景推荐

场景1:中小型Java项目

需求:
- 快速上手
- 不想大改代码
- 基本的链路追踪功能

推荐: Zipkin ⭐⭐⭐⭐

理由:
1. 集成超级简单(引入依赖即可)
2. 轻量级,资源占用少
3. 满足基本需求

场景2:大型微服务项目

需求:
- 100+微服务
- 需要完整的APM功能
- 需要告警
- 不想改代码

推荐: SkyWalking ⭐⭐⭐⭐⭐

理由:
1. 无侵入,Agent模式
2. 功能全面(链路+指标+日志)
3. 告警完善
4. 中文文档友好

场景3:云原生项目

需求:
- Kubernetes部署
- 多语言(Java/Go/Python)
- 标准化(OpenTracing)

推荐: Jaeger ⭐⭐⭐⭐⭐

理由:
1. CNCF毕业项目
2. K8s原生支持
3. OpenTracing标准
4. 性能优异

💡 最佳实践

1. 采样策略

/**
 * 生产环境不要全量采样!
 */

// ❌ 错误:100%采样
spring.sleuth.sampler.probability=1.0

// ✅ 正确:根据流量调整
// 流量小(<1000 QPS): 50%
spring.sleuth.sampler.probability=0.5

// 流量中(1000-10000 QPS): 10%
spring.sleuth.sampler.probability=0.1

// 流量大(>10000 QPS): 1%
spring.sleuth.sampler.probability=0.01

/**
 * 自定义采样策略
 */
@Component
public class CustomSampler extends Sampler {
    
    @Override
    public boolean isSampled(long traceId) {
        // 重要接口100%采样
        if (isImportantApi()) {
            return true;
        }
        
        // 错误请求100%采样
        if (hasError()) {
            return true;
        }
        
        // 其他请求10%采样
        return traceId % 10 == 0;
    }
}

2. 异步上报

/**
 * 不要同步上报,影响性能!
 */

@Configuration
public class TracingConfig {
    
    @Bean
    public AsyncReporter<Span> spanReporter() {
        return AsyncReporter.builder(sender)
            .closeTimeout(1, TimeUnit.SECONDS)
            .messageMaxBytes(1000000)  // 1MB
            .messageTimeout(1, TimeUnit.SECONDS)
            .queuedMaxSpans(10000)     // 队列最大10000
            .queuedMaxBytes(10000000)  // 队列最大10MB
            .build();
    }
}

3. 敏感信息脱敏

@Component
public class SensitiveDataFilter implements SpanHandler {
    
    @Override
    public boolean end(TraceContext context, MutableSpan span, Cause cause) {
        // 脱敏手机号
        span.tags().replaceAll((k, v) -> {
            if (k.contains("phone")) {
                return maskPhone(v);
            }
            return v;
        });
        
        return true;
    }
    
    private String maskPhone(String phone) {
        if (phone != null && phone.length() == 11) {
            return phone.substring(0, 3) + "****" + phone.substring(7);
        }
        return phone;
    }
}

4. 链路与日志关联

@Slf4j
@Service
public class OrderService {
    
    @Autowired
    private Tracer tracer;
    
    public Order createOrder(OrderRequest request) {
        // 获取TraceId
        String traceId = tracer.currentSpan().context().traceIdString();
        
        // 记录日志时带上TraceId
        MDC.put("traceId", traceId);
        log.info("创建订单: {}", request);
        
        try {
            return doCreateOrder(request);
        } finally {
            MDC.remove("traceId");
        }
    }
}

日志格式

# logback-spring.xml
<pattern>
    %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%X{traceId}] %logger{36} - %msg%n
</pattern>

# 输出效果
2024-01-01 10:00:00 [http-nio-8080-exec-1] INFO [550e8400-e29b-41d4-a716] c.e.OrderService - 创建订单: ...

🎯 面试高频问题

Q1:链路追踪如何保证低开销?

A

1. 采样策略

不是所有请求都追踪
生产环境通常采样1%-10%

2. 异步上报

Span数据异步批量上报
不阻塞业务线程

3. 本地聚合

Agent本地聚合数据
减少网络传输

4. 存储优化

使用时序数据库
数据压缩
定期清理

Q2:TraceId如何生成和传递?

A

生成

// 方式1:UUID
String traceId = UUID.randomUUID().toString();

// 方式2:雪花算法
String traceId = SnowflakeIdWorker.generateId();

// 方式3:时间戳+随机数
String traceId = System.currentTimeMillis() + "-" + ThreadLocalRandom.current().nextInt();

传递

HTTP: 通过Header传递
  X-B3-TraceId: xxx
  X-B3-SpanId: yyy
  X-B3-ParentSpanId: zzz

gRPC: 通过Metadata传递

MQ: 通过Message Header传递

Q3:如何追踪异步调用?

A

@Service
public class OrderService {
    
    @Autowired
    private Tracer tracer;
    
    @Autowired
    private Executor executor;
    
    public void asyncProcess() {
        // 获取当前Span
        Span parentSpan = tracer.currentSpan();
        
        executor.execute(() -> {
            // 创建新Span,指定父Span
            Span span = tracer.nextSpan(
                TraceContextOrSamplingFlags.create(parentSpan.context())
            ).start();
            
            try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) {
                // 异步业务逻辑
                doAsyncWork();
            } finally {
                span.finish();
            }
        });
    }
}

🎉 总结

核心要点 ✨

  1. 链路追踪核心概念

    • Trace:完整链路
    • Span:操作单元
    • Annotation:关键事件
  2. 三大系统特点

    • Zipkin:简单轻量
    • SkyWalking:功能全面
    • Jaeger:云原生首选
  3. 选型建议

    • 简单场景 → Zipkin
    • 无侵入需求 → SkyWalking
    • 云原生 → Jaeger

记忆口诀 📝

链路追踪三要素,
Trace、Span和Annotation。
TraceId全局唯一,
SpanId记录每一步。

Zipkin简单易上手,
适合小型快速搞。
SkyWalking功能强,
无侵入来帮大忙。
Jaeger云原生,
K8s首选没商量。

采样策略要合理,
异步上报性能好。
敏感信息要脱敏,
日志关联好排查!

📚 参考资料

  1. Zipkin官方文档
  2. SkyWalking官方文档
  3. Jaeger官方文档
  4. OpenTracing规范
  5. Google Dapper论文

最后送你一句话

"在微服务的世界里,没有链路追踪,就像在黑夜里开车没有车灯。"

愿你的链路清晰可见,问题一眼定位! 🔍✨


表情包时间 🎭

没有链路追踪前:
😱 服务挂了,不知道哪个服务的问题
🤯 请求慢了,不知道慢在哪里
😭 排查问题,凌晨3点还在加班

有了链路追踪后:
🔍 一眼看出问题在哪个服务
⚡ 瞬间定位性能瓶颈
😴 安心睡觉,第二天再处理

程序员的幸福生活从链路追踪开始!