MST

星途 面试题库

面试题:Go中context如何在跨服务调用链路追踪里传递关键信息

请阐述在Go语言中,使用context进行跨服务调用链路追踪时,如何将如Trace ID等关键链路追踪信息从一个服务传递到下一个服务,并且举例说明可能用到的方法和数据结构。
22.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

在Go语言中使用context进行跨服务调用链路追踪时传递关键链路追踪信息(如Trace ID),可以通过以下方式:

  1. 使用context.WithValue

    • 方法context.WithValue函数允许在context中附加键值对数据。我们可以将Trace ID作为值,通过特定的键存储在context中。
    • 数据结构
      type TraceKey struct{}
      const traceIDKey = TraceKey()
      
    • 示例
      package main
      
      import (
          "context"
          "fmt"
      )
      
      func main() {
          ctx := context.Background()
          traceID := "1234567890"
          ctx = context.WithValue(ctx, traceIDKey, traceID)
      
          // 模拟传递到下一个服务
          nextService(ctx)
      }
      
      func nextService(ctx context.Context) {
          traceID, ok := ctx.Value(traceIDKey).(string)
          if ok {
              fmt.Printf("Received Trace ID in next service: %s\n", traceID)
          } else {
              fmt.Println("Trace ID not found in context.")
          }
      }
      
  2. 通过HTTP请求头传递

    • 方法:在发起HTTP请求时,将Trace ID放在请求头中。接收方从HTTP请求头中提取Trace ID并放入context中。
    • 数据结构:无需特定自定义数据结构,使用标准HTTP请求相关结构。
    • 示例
      // 发送方
      package main
      
      import (
          "context"
          "fmt"
          "net/http"
      )
      
      type TraceKey struct{}
      const traceIDKey = TraceKey()
      
      func sendRequest(ctx context.Context) {
          traceID, ok := ctx.Value(traceIDKey).(string)
          if!ok {
              fmt.Println("Trace ID not found in context.")
              return
          }
      
          req, err := http.NewRequestWithContext(ctx, "GET", "http://example.com", nil)
          if err!= nil {
              fmt.Printf("Error creating request: %v\n", err)
              return
          }
          req.Header.Set("X-Trace-ID", traceID)
      
          client := &http.Client{}
          resp, err := client.Do(req)
          if err!= nil {
              fmt.Printf("Error sending request: %v\n", err)
              return
          }
          defer resp.Body.Close()
      }
      
      // 接收方
      package main
      
      import (
          "context"
          "fmt"
          "net/http"
      )
      
      type TraceKey struct{}
      const traceIDKey = TraceKey()
      
      func handler(w http.ResponseWriter, r *http.Request) {
          traceID := r.Header.Get("X-Trace-ID")
          ctx := context.WithValue(r.Context(), traceIDKey, traceID)
      
          // 继续处理,这里可以将ctx传递到后续函数
          fmt.Printf("Received Trace ID in handler: %s\n", traceID)
      }
      
      func main() {
          http.HandleFunc("/", handler)
          fmt.Println("Server listening on :8080")
          http.ListenAndServe(":8080", nil)
      }
      
  3. 使用gRPC元数据传递

    • 方法:在gRPC调用中,使用gRPC的元数据(metadata)来传递Trace ID。客户端将Trace ID放入元数据,服务端从元数据中提取并放入context。
    • 数据结构:使用google.golang.org/grpc/metadata包中的MD结构。
    • 示例
      // 客户端
      package main
      
      import (
          "context"
          "fmt"
          "google.golang.org/grpc"
          "google.golang.org/grpc/metadata"
      )
      
      type TraceKey struct{}
      const traceIDKey = TraceKey()
      
      func main() {
          conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
          if err!= nil {
              fmt.Printf("did not connect: %v", err)
              return
          }
          defer conn.Close()
      
          ctx := context.Background()
          traceID := "1234567890"
          md := metadata.New(map[string]string{"x-trace-id": traceID})
          ctx = metadata.NewOutgoingContext(ctx, md)
      
          // 这里进行gRPC调用
      }
      
      // 服务端
      package main
      
      import (
          "context"
          "fmt"
          "google.golang.org/grpc"
          "google.golang.org/grpc/metadata"
      )
      
      type TraceKey struct{}
      const traceIDKey = TraceKey()
      
      func serverHandler(ctx context.Context) {
          md, ok := metadata.FromIncomingContext(ctx)
          if ok {
              traceIDs := md.Get("x-trace-id")
              if len(traceIDs) > 0 {
                  ctx = context.WithValue(ctx, traceIDKey, traceIDs[0])
                  fmt.Printf("Received Trace ID in server: %s\n", traceIDs[0])
              }
          }
          // 继续处理
      }