面试题答案
一键面试设计方案
- 数据结构:
- 在Go语言中,
context.Context
本身就是一个接口,通常使用context.Background()
作为根上下文,然后基于它衍生出子上下文。为了在分布式系统中传递上下文,我们可以封装一个结构体来携带额外信息。
type DistributedContext struct { Context context.Context TraceID string // 用于分布式追踪 // 其他可能需要传递的全局信息,如认证信息等 }
- 在Go语言中,
- 通信协议:
- HTTP/HTTPS:如果微服务之间通过HTTP进行通信,可以将上下文信息放在HTTP头中传递。例如,将
TraceID
放在X - Trace - ID
头中。
func sendRequest(ctx context.Context, url string) (*http.Response, error) { req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err!= nil { return nil, err } traceID := getTraceIDFromContext(ctx) req.Header.Set("X - Trace - ID", traceID) client := &http.Client{} return client.Do(req) } func receiveRequest(w http.ResponseWriter, r *http.Request) { traceID := r.Header.Get("X - Trace - ID") ctx := context.WithValue(r.Context(), "TraceID", traceID) // 处理请求 }
- gRPC:gRPC支持在请求元数据(metadata)中传递上下文信息。
func sendGRPCRequest(ctx context.Context, client pb.MyServiceClient) (*pb.Response, error) { traceID := getTraceIDFromContext(ctx) md := metadata.New(map[string]string{"trace - id": traceID}) ctx = metadata.NewOutgoingContext(ctx, md) return client.MyMethod(ctx, &pb.Request{}) } func receiveGRPCRequest(ctx context.Context, req *pb.Request) (*pb.Response, error) { md, ok := metadata.FromIncomingContext(ctx) if ok { traceID := md["trace - id"][0] ctx = context.WithValue(ctx, "TraceID", traceID) } // 处理请求 return &pb.Response{}, nil }
- HTTP/HTTPS:如果微服务之间通过HTTP进行通信,可以将上下文信息放在HTTP头中传递。例如,将
- 优化点:
- 缓存上下文:在一些场景下,部分上下文信息在一定时间内不会变化,可以进行缓存,减少重复计算和传递。例如,认证信息如果在短时间内不会过期,可以缓存起来。
- 复用上下文:避免不必要的上下文创建和传递,尽量复用已有的上下文对象,减少内存开销。
关键代码示例
- 上下文传递辅助函数:
func getTraceIDFromContext(ctx context.Context) string { if dc, ok := ctx.(DistributedContext); ok { return dc.TraceID } return "" } func newDistributedContext(ctx context.Context, traceID string) DistributedContext { return DistributedContext{ Context: ctx, TraceID: traceID, } }
- 基于HTTP的完整示例:
package main import ( "context" "fmt" "net/http" ) type DistributedContext struct { Context context.Context TraceID string } func getTraceIDFromContext(ctx context.Context) string { if dc, ok := ctx.(DistributedContext); ok { return dc.TraceID } return "" } func sendRequest(ctx context.Context, url string) (*http.Response, error) { req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err!= nil { return nil, err } traceID := getTraceIDFromContext(ctx) req.Header.Set("X - Trace - ID", traceID) client := &http.Client{} return client.Do(req) } func receiveRequest(w http.ResponseWriter, r *http.Request) { traceID := r.Header.Get("X - Trace - ID") ctx := context.WithValue(r.Context(), "TraceID", traceID) fmt.Fprintf(w, "Received request with TraceID: %s", traceID) } func main() { ctx := newDistributedContext(context.Background(), "123456") resp, err := sendRequest(ctx, "http://localhost:8080") if err!= nil { fmt.Println("Request error:", err) return } defer resp.Body.Close() }
- 基于gRPC的完整示例(简化,仅展示上下文相关部分):
// 假设已经定义了proto文件并生成了pb.go文件 package main import ( "context" "fmt" "google.golang.org/grpc" "google.golang.org/grpc/metadata" pb "your - proto - package" ) type DistributedContext struct { Context context.Context TraceID string } func getTraceIDFromContext(ctx context.Context) string { if dc, ok := ctx.(DistributedContext); ok { return dc.TraceID } return "" } func sendGRPCRequest(ctx context.Context, client pb.MyServiceClient) (*pb.Response, error) { traceID := getTraceIDFromContext(ctx) md := metadata.New(map[string]string{"trace - id": traceID}) ctx = metadata.NewOutgoingContext(ctx, md) return client.MyMethod(ctx, &pb.Request{}) } func receiveGRPCRequest(ctx context.Context, req *pb.Request) (*pb.Response, error) { md, ok := metadata.FromIncomingContext(ctx) if ok { traceID := md["trace - id"][0] ctx = context.WithValue(ctx, "TraceID", traceID) } fmt.Printf("Received request with TraceID: %s\n", traceID) return &pb.Response{}, nil } func main() { conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) if err!= nil { fmt.Println("Connection error:", err) return } defer conn.Close() client := pb.NewMyServiceClient(conn) ctx := newDistributedContext(context.Background(), "123456") resp, err := sendGRPCRequest(ctx, client) if err!= nil { fmt.Println("GRPC request error:", err) return } fmt.Println("GRPC response:", resp) }