跳到主要内容

链路追踪

问题

分布式系统中如何追踪一个请求的完整调用链?Go 中如何集成?

答案

核心概念

概念说明
Trace一个完整请求的调用链,全局唯一 TraceID
Span调用链中的一个操作,有 SpanID 和 ParentSpanID
Context Propagation在服务间传递 TraceID / SpanID

OpenTelemetry 集成

OpenTelemetry(OTel)是现在的标准:

import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/sdk/trace"
)

func initTracer() func() {
exporter, _ := otlptracehttp.New(context.Background(),
otlptracehttp.WithEndpoint("jaeger:4318"),
otlptracehttp.WithInsecure(),
)

tp := trace.NewTracerProvider(
trace.WithBatcher(exporter),
trace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.ServiceName("user-service"),
)),
)

otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})

return func() { tp.Shutdown(context.Background()) }
}

gRPC 集成

import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"

// 服务端
server := grpc.NewServer(
grpc.StatsHandler(otelgrpc.NewServerHandler()),
)

// 客户端
conn, _ := grpc.Dial(target,
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
)

Gin 集成

import "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"

r := gin.Default()
r.Use(otelgin.Middleware("api-gateway"))

手动创建 Span

func GetUserProfile(ctx context.Context, userID string) (*Profile, error) {
// 创建子 Span
ctx, span := otel.Tracer("user-service").Start(ctx, "GetUserProfile")
defer span.End()

// 添加属性
span.SetAttributes(attribute.String("user.id", userID))

// 调用数据库(也会创建子 Span)
profile, err := queryDB(ctx, userID)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}

return profile, nil
}

Context 传播

TraceID 通过 HTTP Header / gRPC Metadata 在服务间传递:

HTTP Header:
traceparent: 00-{traceId}-{spanId}-01

gRPC Metadata:
traceparent → 00-abc123-def456-01
关键

所有函数调用都要传递 ctx context.Context,这样 Span 才能形成父子关系。丢失 ctx = 断链。


常见面试问题

Q1: TraceID 是怎么跨服务传递的?

答案:通过 Context Propagation。HTTP 服务在 Header 中注入 traceparent;gRPC 在 Metadata 中注入。下游服务提取 Header,恢复 Trace 上下文,创建新 Span 时自动成为子 Span。

Q2: OpenTelemetry vs Jaeger vs Zipkin?

答案

  • OpenTelemetry:统一标准(API + SDK + 协议),现在的首选
  • Jaeger:Trace 后端存储和 UI,接收 OTel 数据
  • Zipkin:类似 Jaeger 的后端

推荐组合:OTel SDK → Jaeger Backend → Jaeger UI

相关链接