GRPC元数据在Go中未更新。

huangapple go评论90阅读模式
英文:

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)
}

huangapple
  • 本文由 发表于 2022年10月19日 17:14:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/74122776.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定