面试题答案
一键面试在Go语言中使用context进行跨服务调用链路追踪时传递关键链路追踪信息(如Trace ID),可以通过以下方式:
-
使用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.") } }
- 方法:
-
通过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) }
-
使用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]) } } // 继续处理 }