使用流发送编码为protobuf的元数据。

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

Sending protobuf-encoded metadata with a stream

问题

关于在启动客户端流式gRPC时附加元数据(“初始”请求)的问题已经有人提过了(这里这里),但是一些答案表明这是不可能的,并建议使用oneof,其中第一个请求向服务器包含了所需的元数据,而后续的请求包含了服务器要处理的实际数据。我想知道是否可以安全地使用所选择的二进制编码对元数据进行编码,并将其发送到服务器,然后可以从Context对象中提取并反序列化为有意义的数据。我相当确定对于基于文本的编码(如JSON),这是完全可以的。但是对于protobuf呢?假设我们定义了以下的服务:

service MyService {
  rpc ChitChat (stream ChatMessage) returns (stream ChatMessage);
}

message ChatMessage {
  // ...
}

message Meta {
  // ...
}

我们可以在请求中包含一个Meta对象:

meta := &pb.Meta{
	// ...
}
metab, err := proto.Marshal(meta)
if err != nil {
	log.Fatalf("marshaling error: %v", err)
}
newCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("meta-bin", string(metab)))
// ...ChitChat(newCtx)

然后在服务器端访问它:

func (s *server) ChitChat(stream pb.MyService_ChitChatServer) error {
	md, ok := metadata.FromIncomingContext(stream.Context())
	if !ok {
		return fmt.Errorf("no metadata received")
	}
	metaStr := md.Get("meta-bin")
	if len(metaStr) != 1 {
		return fmt.Errorf("expected 1 md; got: %v", len(metaStr))
	}
	meta := new(pb.Meta)
	if err := proto.Unmarshal([]byte(metaStr[0]), meta); err != nil {
		return fmt.Errorf("error during deserialization: %v", err)
	}

	// ...
	return nil
}

看起来这个方法运行得很好 - 我有什么遗漏的吗?使用这种方法容易出错吗?

英文:

Questions about attaching a piece of metadata (an "initial" request) when initiating a client-side streaming gRPC have already been asked before (here, here), but some answers are suggesting that it's not possible and suggest using oneof where first request towards a server contains the metadata in question, and subsequent requests contain the actual data to be processed by the server. I'm wondering if it's safe to encode metadata with a binary encoding of choice and send it to the server where it can be extracted from the Context object and deserialized back into meaningful data. I'm fairly certain that it's perfectly fine when it comes to text-based encodings such as JSON. But what about protobuf? Assuming we define our service like so:

service MyService {
  rpc ChitChat (stream ChatMessage) returns (stream ChatMessage);
}

message ChatMessage {
  // ...
}

message Meta {
  // ...
}

We can include a Meta object in the request:

meta := &pb.Meta{
	// ...
}
metab, err := proto.Marshal(meta)
if err != nil {
	log.Fatalf("marshaling error: %v", err)
}
newCtx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs("meta-bin", string(metab)))
// ...ChitChat(newCtx)

And access it on the server side:

func (s *server) ChitChat(stream pb.MyService_ChitChatServer) error {
	md, ok := metadata.FromIncomingContext(stream.Context())
	if !ok {
		return fmt.Errorf("no metadata received")
	}
	metaStr := md.Get("meta-bin")
	if len(metaStr) != 1 {
		return fmt.Errorf("expected 1 md; got: %v", len(metaStr))
	}
	meta := new(pb.Meta)
	if err := proto.Unmarshal([]byte(metaStr[0]), meta); err != nil {
		return fmt.Errorf("error during deserialization: %v", err)
	}

	// ...
	return nil
}

It appears to be working quite well - am I missing something? How easy is it to shoot yourself in the foot with this approach?

答案1

得分: 1

是的,gRPC支持二进制头部,所以这种方法并不是无效的;虽然这种方式可能不太明确,但是oneof方式也是如此,所以...没有太大的区别。

英文:

Yes, gRPC supports binary headers, so this approach isn't invalid; it is a little less clear that it is expected, but then: that's true for the oneof approach too, so ... not much difference there.

huangapple
  • 本文由 发表于 2022年10月22日 16:32:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/74162099.html
匿名

发表评论

匿名网友

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

确定