Jaeger和OpenTelemetry跨服务传递详解
常见技术问题 刘宇帅 21天前 阅读量: 37
目录
- 跨服务传递概述
- 上下文传播的核心概念
- Jaeger 的上下文传播机制
- OpenTelemetry 的上下文传播机制
- Jaeger 与 OpenTelemetry 的上下文传播集成
- 示例代码与配置
- 最佳实践
- 总结
- 参考资料
一、跨服务传递概述
在分布式系统中,一个用户请求可能会经过多个服务和组件的协作才能完成。为了能够全面监控和诊断整个请求链,上下文传播(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标准化,使用
traceparent
和tracestate
头部字段进行上下文传播。 - B3 Propagation:由Twitter开发,使用一组特定的HTTP Headers,如
X-B3-TraceId
、X-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。
-
Key:
uber-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后端。
集成步骤
-
添加依赖:在Spring Boot应用中引入OpenTelemetry和Jaeger的相关依赖。
-
配置上下文传播:选择和配置上下文传播标准(如W3C Trace Context)。
-
配置Exporters:设置Jaeger Exporter的Endpoint和其他参数。
-
自动化 Instrumentation:启用自动化Instrumentations,自动捕获常见操作的Trace信息。
- 手动 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
- Jaeger UI:访问 http://localhost:16686 查看Trace数据。
步骤 5:运行和验证
-
启动Spring Boot应用:
mvn spring-boot:run
-
访问服务:
-
自动化追踪端点:
curl http://localhost:8080/auto
-
手动追踪端点:
curl http://localhost:8080/manual
-
-
查看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:运行和验证
-
启动Spring Boot应用:
mvn spring-boot:run
-
访问服务:
curl http://localhost:8080/sleuth
-
查看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();
}
}
}
验证上下文传播
-
启动Jaeger后端(前述步骤)。
-
启动服务B:
mvn spring-boot:run -Dspring-boot.run.arguments=--server.port=8081
-
启动服务A:
mvn spring-boot:run
-
访问服务A:
curl http://localhost:8080/call-service-b
-
查看Trace数据:
在Jaeger UI中,选择
service-a
,可以看到Trace包括服务A的Span和服务B的Span,且Span之间有正确的父子关系。
七、最佳实践
-
使用标准化的上下文传播协议:
- 优先选择W3C Trace Context,因为它是最新的标准,并且被广泛支持。
- 对于现有系统,B3 Propagation也可以继续使用,但建议逐步迁移到W3C Trace Context。
-
自动化 Instrumentation 优先:
- 利用Spring Cloud Sleuth或OpenTelemetry的自动化工具,减少手动管理Span的工作量。
- 仅在自动化工具无法覆盖的场景下,进行手动Instrumentations。
-
统一上下文传播配置:
- 在整个分布式系统中,确保所有服务使用相同的上下文传播协议和配置,以避免Trace信息丢失或不一致。
-
保护Trace数据的隐私和安全:
- 避免在Trace数据中记录敏感信息,如用户密码、个人身份信息等。
- 使用加密协议(如TLS)保护Trace数据在传输过程中的安全性。
-
监控和优化采样策略:
- 根据系统负载和追踪需求,合理配置采样率,平衡数据量和追踪覆盖率。
- 动态调整采样策略,尤其在高负载情况下,降低采样率以减少性能开销。
-
充分利用可视化工具进行分析:
- 使用Jaeger或Zipkin的UI界面,分析Trace数据,识别性能瓶颈和故障点。
- 定期审查Trace数据,优化系统架构和服务调用链。
八、总结
Jaeger和OpenTelemetry是现代分布式系统中关键的链路追踪工具,协同工作能够实现全面而灵活的Trace上下文传播与管理。
-
Jaeger:
- 作为一个强大的追踪后端,负责Trace数据的收集、存储和可视化。
- 提供丰富的查询和分析功能,帮助开发者进行故障诊断和性能优化。
- OpenTelemetry:
- 提供统一的API和SDK,支持多种上下文传播标准(如W3C Trace Context和B3 Propagation)。
- 支持自动化和手动Instrumentations,简化Trace数据的生成与管理。
- 通过Exporters将Trace数据发送到Jaeger等后端系统,实现高效的数据流转。
关键要点:
-
标准化上下文传播:采用W3C Trace Context等标准化协议,确保Trace信息在不同服务间的一致性和连贯性。
-
自动化与手动结合:利用自动化工具捕获常见操作的Trace信息,同时在必要时进行手动Span管理,覆盖自定义业务逻辑。
-
配置与集成:正确配置OpenTelemetry和Jaeger,确保Trace数据的正确传输与存储。
- 安全与隐私:保护Trace数据的敏感信息,确保数据传输的安全性和完整性。
通过深入理解和正确配置Jaeger与OpenTelemetry的上下文传播机制,开发者可以构建高效、可靠且可扩展的分布式追踪系统,提升应用程序的可观测性和维护性。
九、参考资料
- Jaeger 官方文档
- OpenTelemetry 官方文档
- W3C Trace Context 标准
- Spring Cloud Sleuth 官方文档
- OpenTelemetry Collector 文档
- CNCF Jaeger 项目
- CNCF OpenTelemetry 项目
- Distributed Tracing 101
- B3 Propagation Specification
- Spring Boot 与 Jaeger 集成教程
- OpenTelemetry 与 Spring Boot 集成教程
希望这份详尽的解读能帮助你全面理解Jaeger和OpenTelemetry在跨服务传递中的工作原理,并在实际项目中高效应用它们,实现强大的分布式追踪能力。