英文:
GRPC metadata not updating in Go
问题
我正在尝试编写一个一元拦截器,用于向传入的请求添加请求ID:
func RequestIDInterceptor(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (val interface{}, err error) {
newCtx := ctx
newCtx = createAndSetMetadata(newCtx, metadata.FromIncomingContext, metadata.NewIncomingContext)
newCtx = createAndSetMetadata(newCtx, metadata.FromOutgoingContext, metadata.NewOutgoingContext)
md, ok := metadata.FromOutgoingContext(newCtx)
fmt.Printf("更新后的元数据:%v,是否找到:%t\n", md, ok)
return handler(newCtx, req)
}
func createAndSetMetadata(ctx context.Context, getter func(context.Context) (metadata.MD, bool),
setter func(context.Context, metadata.MD) context.Context) context.Context {
meta, ok := getter(ctx)
fmt.Printf("是否找到:%t,元数据:%v\n", ok, meta)
if !ok {
meta = metadata.New(map[string]string{})
ctx = setter(ctx, meta)
}
meta.Set("X-Request-Id", "TEST_REQUEST_ID")
fmt.Printf("元数据:%v\n", meta)
return ctx
}
当请求不包含元数据时,此代码按预期工作(RequestIDInterceptor(context.Background(), nil, nil, handler)
),但是如果上下文包含元数据,则ID将不会被填充。实际上,输出如下所示:
没有元数据
是否找到:false,元数据:map[]
元数据:map[x-request-id:[TEST_REQUEST_ID]]
是否找到:false,元数据:map[]
元数据:map[x-request-id:[TEST_REQUEST_ID]]
更新后的元数据:map[x-request-id:[TEST_REQUEST_ID]],是否找到:true
有元数据
是否找到:true,元数据:map[]
元数据:map[x-request-id:[TEST_REQUEST_ID]]
是否找到:true,元数据:map[]
元数据:map[x-request-id:[TEST_REQUEST_ID]]
更新后的元数据:map[],是否找到:true
据我理解,发生这种情况的原因可能是metadata.MD
是按值传递而不是按引用传递,因此在createAndSetMetadata
返回时会丢弃更新。我该如何修复这段代码呢?
英文:
I'm trying to write a unary interceptor to add a request ID to incoming requests:
func RequestIDInterceptor(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (val interface{}, err error) {
newCtx := ctx
newCtx = createAndSetMetadata(newCtx, metadata.FromIncomingContext, metadata.NewIncomingContext)
newCtx = createAndSetMetadata(newCtx, metadata.FromOutgoingContext, metadata.NewOutgoingContext)
md, ok := metadata.FromOutgoingContext(newCtx)
fmt.Printf("After update. Metadata: %v, Found: %t\n", md, ok)
return handler(newCtx, req)
}
func createAndSetMetadata(ctx context.Context, getter func(context.Context) (metadata.MD, bool),
setter func(context.Context, metadata.MD) context.Context) context.Context {
meta, ok := getter(ctx)
fmt.Printf("Found? %t, Metadata: %v\n", ok, meta)
if !ok {
meta = metadata.New(map[string]string{})
ctx = setter(ctx, meta)
}
meta.Set("X-Request-Id", "TEST_REQUEST_ID")
fmt.Printf("Metadata: %v\n", meta)
return ctx
}
This code works as expected when the request doesn't include metadata (RequestIDInterceptor(context.Background(), nil, nil, handler)
) but if the context includes metadata then the ID won't populated. Essentially, the output looks like this:
WITHOUT METADATA
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[x-request-id:[TEST_REQUEST_ID]], Found: true
WITH METADATA
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[], Found: true
To my understanding, the reason this is probably happening is that metadata.MD
is being passed by value rather than by reference, so updates are being discarded when createAndSetMetadata
returns. How can I fix this code?
答案1
得分: 1
问题在于metadata.FromOutgoingContext
实际上返回的是上下文中找到的metadata.MD
的深拷贝。你可以查看源代码,截至到google.golang.org/grpc@v1.50
,这仍然是正确的。
因此,直接对其进行写入(就像对map进行操作一样)并不会影响存储在上下文中的MD
。
你必须再次调用setter来传播新的值:
func createAndSetMetadata(/* args */) context.Context {
// ...
return setter(ctx, meta)
}
英文:
The issue is that metadata.FromOutgoingContext
effectively returns a deep copy of the metadata.MD
found in the context. You can check the sources, it's still true as of google.golang.org/grpc@v1.50
.
So directly writing to it (the copy) as you would do with a map simply doesn't affect the MD
that is stored in the context.
You have to call the setter again to propagate the new values:
func createAndSetMetadata(/* args */) context.Context {
// ...
return setter(ctx, meta)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论