英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论