Jaeger和OpenTelemetry跨服务传递详解

常见技术问题 刘宇帅 21天前 阅读量: 37

目录

  1. 跨服务传递概述
  2. 上下文传播的核心概念
  3. Jaeger 的上下文传播机制
  4. OpenTelemetry 的上下文传播机制
  5. Jaeger 与 OpenTelemetry 的上下文传播集成
  6. 示例代码与配置
  7. 最佳实践
  8. 总结
  9. 参考资料

一、跨服务传递概述

分布式系统中,一个用户请求可能会经过多个服务和组件的协作才能完成。为了能够全面监控和诊断整个请求链,上下文传播(Context Propagation)机制至关重要。它确保每个服务都能关联到同一个Trace,从而形成完整的调用链(Trace),并在系统的各个部分生成相应的Span。

关键目标

  • Trace 连贯性:确保整个请求链中的Trace ID和Span ID的一致性。
  • 层次关系维护:正确建立Span之间的父子关系,反映调用树的结构。
  • 标准化传递:采用标准化的协议和格式,确保跨语言和跨平台的兼容性。

二、上下文传播的核心概念

1. Trace ID 和 Span ID

  • Trace ID:唯一标识一个完整的Trace,即一个请求在整个分布式系统中的生命周期。所有属于同一Trace的Span共享相同的Trace ID。

  • Span ID:唯一标识Trace中的一个Span,代表一个具体的操作或任务。每个Span都有自己的Span ID。

2. Parent Span ID

  • Parent Span ID:指向当前Span的父Span的ID,用于建立Span之间的层次关系,形成调用树。

  • 根Span:没有Parent Span ID,是Trace中的起点。

3. Trace Context 标准

为了解决不同追踪系统和语言间的兼容性问题,制定了多个Trace Context标准,最主要的包括:

  • W3C Trace Context:由W3C标准化,使用traceparenttracestate头部字段进行上下文传播。

  • B3 Propagation:由Twitter开发,使用一组特定的HTTP Headers,如X-B3-TraceIdX-B3-SpanId等。

三、Jaeger 的上下文传播机制

1. Jaeger 使用的上下文传播协议

Jaeger本身并不限制上下文传播协议,但通常采用以下两种主要方式:

  • HTTP Headers:适用于基于HTTP的服务间通信。

  • gRPC Metadata:适用于基于gRPC的通信。

2. HTTP Headers 中的 Trace 信息

在HTTP请求中,Jaeger使用特定的Headers来传递Trace上下文信息:

  • uber-trace-id:包含Trace ID、Span ID、Parent Span ID和采样标志。

    格式示例:TraceID:SpanID:ParentSpanID:Flags

    例如:1e223c4d5a6b7c8d9e0f:3e4f5g6h:0000000000000000:1

3. gRPC Metadata 中的 Trace 信息

在gRPC请求中,Jaeger通过Metadata传递Trace上下文信息,类似于HTTP Headers。

  • Keyuber-trace-id

  • Value:与HTTP Headers相同的格式。

4. 实现上下文传播的步骤

步骤 1:在客户端生成Trace和Span

当一个服务作为客户端向下游服务发起请求时,它需要在请求中附加当前Trace的上下文信息。

步骤 2:传递Trace Context

将当前Trace ID和Span ID通过HTTP Headers或gRPC Metadata附加到请求中。

步骤 3:下游服务接收并关联Trace

下游服务接收到请求后,解析Trace上下文信息,创建新的Span,并将其关联到上游的Trace和Span。

步骤 4:继续传播

下游服务在继续调用其他服务时,重复上述步骤,确保Trace上下文在整个请求链中保持一致。


四、OpenTelemetry 的上下文传播机制

1. OpenTelemetry 支持的上下文传播标准

OpenTelemetry设计为支持多种上下文传播标准,以实现更广泛的兼容性。主要包括:

  • W3C Trace Context

  • B3 Propagation

  • B3 Single Header

  • Jaeger Propagation(不常用,因为OpenTelemetry通常替代Jaeger的传播)

2. W3C Trace Context

作为最新的标准,W3C Trace Context旨在提供一种跨语言和跨平台的统一Trace上下文传播机制。

  • Headers

    • traceparent:包含Trace ID、Parent Span ID和Trace Flags。

    格式:traceparent: {version}-{trace-id}-{parent-id}-{trace-flags}

    例如:traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

    • tracestate:用于传递供应商特定的Trace信息,可选。

3. B3 Propagation

虽然W3C Trace Context是推荐的标准,但B3 Propagation仍然广泛使用,特别是在现有系统中。

  • Headers

    • X-B3-TraceId:Trace ID

    • X-B3-SpanId:Span ID

    • X-B3-ParentSpanId:Parent Span ID(可选)

    • X-B3-Sampled:采样标志(0或1)

4. 实现上下文传播的步骤

步骤 1:配置上下文传播格式

在OpenTelemetry SDK中配置使用的上下文传播格式(W3C Trace Context或B3 Propagation)。

步骤 2:自动化 Instrumentation

通过自动化Instrumentations,OpenTelemetry会自动拦截常见的操作(如HTTP请求、数据库查询等),并自动附加Trace上下文到请求中。

步骤 3:手动 Instrumentation

对于自动化工具无法覆盖的自定义操作,开发者可以手动创建Span并管理Trace上下文。

步骤 4:导出Trace数据

通过配置Exporters(如Jaeger Exporter),将Trace数据发送到后端系统。


五、Jaeger 与 OpenTelemetry 的上下文传播集成

1. 使用 OpenTelemetry 与 Jaeger 结合

OpenTelemetry作为一个统一的可观测性框架,可以与Jaeger紧密集成,通过Jaeger Exporter将Trace数据发送到Jaeger后端。

集成步骤

  1. 添加依赖:在Spring Boot应用中引入OpenTelemetry和Jaeger的相关依赖。

  2. 配置上下文传播:选择和配置上下文传播标准(如W3C Trace Context)。

  3. 配置Exporters:设置Jaeger Exporter的Endpoint和其他参数。

  4. 自动化 Instrumentation:启用自动化Instrumentations,自动捕获常见操作的Trace信息。

  5. 手动 Instrumentation(如需):在特定业务逻辑中手动创建和管理Span。

2. 配置示例:Spring Boot 应用

以下示例展示了如何在Spring Boot应用中配置OpenTelemetry与Jaeger的集成,实现跨服务的Trace上下文传播。

步骤 1:添加依赖

pom.xml中添加OpenTelemetry和Jaeger相关依赖:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- OpenTelemetry API -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- OpenTelemetry SDK -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- OpenTelemetry Exporter for Jaeger -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-jaeger</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- OpenTelemetry Instrumentation for Spring Boot -->
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-spring-boot-starter</artifactId>
        <version>1.15.0</version>
    </dependency>
</dependencies>

步骤 2:配置 OpenTelemetry

src/main/resources/application.properties中添加以下配置:

# OpenTelemetry Configuration
otel.exporter.jaeger.endpoint=http://localhost:14250
otel.traces.sampler=always_on
otel.resource.attributes=service.name=my-java-service

# Choose propagation format: tracecontext (W3C Trace Context) or b3
otel.propagators=tracecontext

说明

  • otel.exporter.jaeger.endpoint:Jaeger Collector的gRPC端点。
  • otel.traces.sampler:采样策略,always_on表示全采样。根据需求调整。
  • otel.resource.attributes:定义服务名称等资源属性。
  • otel.propagators:选择上下文传播格式,tracecontext表示使用W3C Trace Context。

步骤 3:编写控制器

创建一个简单的REST控制器,自动和手动创建Span:

package com.example.demo;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.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 TraceController {

    @Autowired
    private Tracer tracer;

    @GetMapping("/auto")
    public String autoTrace() {
        // 这个端点通过自动化 Instrumentation 自动创建Span
        return "Auto Traced Endpoint";
    }

    @GetMapping("/manual")
    public String manualTrace() {
        // 手动创建一个新的 Span
        Span span = tracer.spanBuilder("manual-span").startSpan();
        try {
            // 执行业务逻辑
            return "Manually Traced Endpoint";
        } finally {
            span.end();
        }
    }
}

步骤 4:启动 Jaeger 后端

使用Docker启动Jaeger:

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 14250:14250 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.42

步骤 5:运行和验证

  1. 启动Spring Boot应用

    mvn spring-boot:run
  2. 访问服务

    • 自动化追踪端点:

      curl http://localhost:8080/auto
    • 手动追踪端点:

      curl http://localhost:8080/manual
  3. 查看Trace数据

    在Jaeger UI中,选择my-java-service,可以看到通过自动化和手动Instrumentations生成的Trace和Span。


六、示例代码与配置

1. 使用 Spring Cloud Sleuth 与 Jaeger 进行上下文传播

以下示例展示了如何在Spring Boot应用中使用Spring Cloud Sleuth与Jaeger实现上下文传播。

步骤 1:添加依赖

pom.xml中添加Spring Cloud Sleuth和Jaeger相关依赖:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Cloud Sleuth -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-sleuth</artifactId>
    </dependency>

    <!-- Zipkin Starter (Jaeger通过OTLP Exporter接收数据,不直接支持) -->
    <!-- 如果需要使用Jaeger的OTLP Collector,可以配置相关Exporter -->
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2021.0.3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

步骤 2:配置 application.properties

配置Spring Cloud Sleuth与Jaeger的连接(使用OTLP Exporter):

# Spring Cloud Sleuth Configuration
spring.sleuth.sampler.probability=1.0

# OpenTelemetry Exporter Configuration
otel.exporter.otlp.endpoint=http://localhost:4317
otel.exporter.otlp.protocol=grpc
otel.traces.sampler=always_on
otel.resource.attributes=service.name=sleuth-jaeger-service

# Context Propagation
otel.propagators=tracecontext,b3

步骤 3:编写控制器

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SleuthController {

    @GetMapping("/sleuth")
    public String sleuthTrace() {
        // Sleuth自动创建Span
        return "Sleuth Traced Endpoint";
    }
}

步骤 4:启动 Jaeger 后端

与前述相同,使用Docker启动Jaeger的OTLP接收器:

docker run -d --name jaeger \
  -e COLLECTOR_OTLP_ENABLED=true \
  -p 16686:16686 \
  -p 4317:4317 \
  jaegertracing/all-in-one:1.42

步骤 5:运行和验证

  1. 启动Spring Boot应用

    mvn spring-boot:run
  2. 访问服务

    curl http://localhost:8080/sleuth
  3. 查看Trace数据

    在Jaeger UI中,选择sleuth-jaeger-service,查看生成的Trace。

2. 使用 OpenTelemetry 的自动化 Instrumentation

以下示例展示了如何在Spring Boot应用中使用OpenTelemetry的自动化Instrumentation与Jaeger实现上下文传播。

步骤 1:添加依赖

pom.xml中添加OpenTelemetry和Jaeger的相关依赖(前述示例已展示)。

步骤 2:配置 OpenTelemetry

如前述示例所示,在application.properties中进行配置。

步骤 3:编写控制器

如前述示例所示,自动化和手动创建Span。

3. 手动创建 Span 的示例

以下示例展示了如何在服务间调用中手动传递Trace上下文,并确保Trace的连贯性。

步骤 1:创建两个Spring Boot服务

  • 服务A:作为客户端,发起HTTP请求到服务B。

  • 服务B:作为服务器,接收请求并参与同一个Trace。

服务A

添加依赖
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- OpenTelemetry API -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-api</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- OpenTelemetry SDK -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-sdk</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- OpenTelemetry Exporter for Jaeger -->
    <dependency>
        <groupId>io.opentelemetry</groupId>
        <artifactId>opentelemetry-exporter-jaeger</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- OpenTelemetry Spring Boot Starter -->
    <dependency>
        <groupId>io.opentelemetry.instrumentation</groupId>
        <artifactId>opentelemetry-spring-boot-starter</artifactId>
        <version>1.15.0</version>
    </dependency>

    <!-- Spring Boot Starter Actuator (可选,用于监控) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
</dependencies>
配置 application.properties
# OpenTelemetry Configuration
otel.exporter.jaeger.endpoint=http://localhost:14250
otel.traces.sampler=always_on
otel.resource.attributes=service.name=service-a

# Context Propagation
otel.propagators=tracecontext
编写控制器
package com.example.servicea;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;

@RestController
public class ServiceAController {

    @Autowired
    private Tracer tracer;

    @GetMapping("/call-service-b")
    public String callServiceB() {
        // 获取当前上下文
        Span currentSpan = Span.current();
        // 开始一个新的Span
        Span span = tracer.spanBuilder("call-service-b").startSpan();
        try {
            // 业务逻辑
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders headers = new HttpHeaders();
            // 将Trace上下文添加到请求头中
            headers.putAll(currentSpan.getSpanContext().toSpanId().asMap());
            HttpEntity<String> entity = new HttpEntity<>(headers);
            ResponseEntity<String> response = restTemplate.exchange("http://localhost:8081/service-b", 
                org.springframework.http.HttpMethod.GET, entity, String.class);
            return "Service A called Service B: " + response.getBody();
        } finally {
            span.end();
        }
    }
}

注意:实际应用中,应使用拦截器或拦截机制自动处理Trace上下文的传递,避免手动操作Headers。

服务B

添加依赖

同服务A。

配置 application.properties
# OpenTelemetry Configuration
otel.exporter.jaeger.endpoint=http://localhost:14250
otel.traces.sampler=always_on
otel.resource.attributes=service.name=service-b

# Context Propagation
otel.propagators=tracecontext
编写控制器
package com.example.serviceb;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.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 ServiceBController {

    @Autowired
    private Tracer tracer;

    @GetMapping("/service-b")
    public String serviceB() {
        // 自动关联到上游Trace
        Span span = tracer.spanBuilder("service-b-operation").startSpan();
        try {
            // 业务逻辑
            return "Service B Response";
        } finally {
            span.end();
        }
    }
}

验证上下文传播

  1. 启动Jaeger后端(前述步骤)。

  2. 启动服务B

    mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8081
  3. 启动服务A

    mvn spring-boot:run
  4. 访问服务A

    curl http://localhost:8080/call-service-b
  5. 查看Trace数据

    在Jaeger UI中,选择service-a,可以看到Trace包括服务A的Span和服务B的Span,且Span之间有正确的父子关系。


七、最佳实践

  1. 使用标准化的上下文传播协议

    • 优先选择W3C Trace Context,因为它是最新的标准,并且被广泛支持。
    • 对于现有系统,B3 Propagation也可以继续使用,但建议逐步迁移到W3C Trace Context。
  2. 自动化 Instrumentation 优先

    • 利用Spring Cloud Sleuth或OpenTelemetry的自动化工具,减少手动管理Span的工作量。
    • 仅在自动化工具无法覆盖的场景下,进行手动Instrumentations。
  3. 统一上下文传播配置

    • 在整个分布式系统中,确保所有服务使用相同的上下文传播协议和配置,以避免Trace信息丢失或不一致。
  4. 保护Trace数据的隐私和安全

    • 避免在Trace数据中记录敏感信息,如用户密码、个人身份信息等。
    • 使用加密协议(如TLS)保护Trace数据在传输过程中的安全性。
  5. 监控和优化采样策略

    • 根据系统负载和追踪需求,合理配置采样率,平衡数据量和追踪覆盖率。
    • 动态调整采样策略,尤其在高负载情况下,降低采样率以减少性能开销。
  6. 充分利用可视化工具进行分析

    • 使用Jaeger或Zipkin的UI界面,分析Trace数据,识别性能瓶颈和故障点。
    • 定期审查Trace数据,优化系统架构和服务调用链。

八、总结

JaegerOpenTelemetry是现代分布式系统中关键的链路追踪工具,协同工作能够实现全面而灵活的Trace上下文传播与管理。

  • Jaeger

    • 作为一个强大的追踪后端,负责Trace数据的收集、存储和可视化。
    • 提供丰富的查询和分析功能,帮助开发者进行故障诊断和性能优化。
  • OpenTelemetry
    • 提供统一的API和SDK,支持多种上下文传播标准(如W3C Trace Context和B3 Propagation)。
    • 支持自动化和手动Instrumentations,简化Trace数据的生成与管理。
    • 通过Exporters将Trace数据发送到Jaeger等后端系统,实现高效的数据流转。

关键要点

  1. 标准化上下文传播:采用W3C Trace Context等标准化协议,确保Trace信息在不同服务间的一致性和连贯性。

  2. 自动化与手动结合:利用自动化工具捕获常见操作的Trace信息,同时在必要时进行手动Span管理,覆盖自定义业务逻辑。

  3. 配置与集成:正确配置OpenTelemetry和Jaeger,确保Trace数据的正确传输与存储。

  4. 安全与隐私:保护Trace数据的敏感信息,确保数据传输的安全性和完整性。

通过深入理解和正确配置Jaeger与OpenTelemetry的上下文传播机制,开发者可以构建高效、可靠且可扩展的分布式追踪系统,提升应用程序的可观测性和维护性。


九、参考资料

希望这份详尽的解读能帮助你全面理解JaegerOpenTelemetry在跨服务传递中的工作原理,并在实际项目中高效应用它们,实现强大的分布式追踪能力。

提示

功能待开通!


暂无评论~