27

我想创建一个简单的gRPC端点,用户可以上传他/她的图片。协议缓冲区声明如下:

message UploadImageRequest {
    AuthToken auth = 1;
    // An enum with either JPG or PNG
    FileType image_format = 2;
    // Image file as bytes
    bytes image = 3;
}

不管 gRPC 文档中的警告如何,这种上传图片(和接收图片)的方法仍然可以吗?

如果不是,那么使用标准格式上传图片并存储图像文件位置是否是更好的方法(标准)?

4

2 回答 2

23

对于大型二进制传输,标准方法是分块。分块有两个目的:

  1. 减少处理每条消息所需的最大内存量
  2. 为恢复部分上传提供边界。

对于您的用例#2 可能不是很有必要。

在 gRPC 中,客户端流式调用允许相当自然的分块,因为它具有流控制、流水线,并且易于在客户端和服务器代码中维护上下文。如果您关心恢复部分上传,那么双向流式传输效果很好,因为服务器可以响应客户端可以用来恢复的进度确认。

使用单独的 RPC 进行分块也是可能的,但有更多的复杂性。在进行负载平衡时,可能需要后端与其他后端协调每个块。如果您以串行方式上传块,则网络延迟会降低上传速度,因为您大部分时间都在等待接收来自服务器的响应。然后,您要么必须并行上传(但并行上传多少个?),要么增加块大小。但是增加块大小会增加处理每个块所需的内存,并增加恢复失败上传的粒度。并行上传还需要服务器处理乱序上传。

于 2016-01-24T23:01:42.873 回答
18

问题中提供的解决方案不适用于大尺寸文件。它仅适用于较小的图像尺寸。更好和标准的方法是使用分块。grpc 支持流式传输内置。因此以块的形式发送相当容易

syntax = 'proto3'

message UploadImageRequest{
    bytes image = 1;

}

rpc UploadImage(stream UploadImageRequest) returns (Ack); 

通过上述方式,我们可以使用流式处理进行分块。

对于分块,所有语言都提供了自己的基于块大小来分块文件的方法。

需要注意的事项:

您需要处理分块逻辑,流有助于自然发送。如果您想发送元数据,也有三种方法。

1:使用以下结构

message UploadImageRequest{
    AuthToken auth = 1;
    FileType image_format = 2;
    bytes image = 3;
}

rpc UploadImage(stream UploadImageRequest) returns (Ack); 

这里字节仍然是块,对于第一个块发送 AuthToken 和 FileType,对于所有其他请求,只是不发送这些元数据。

2:你也可以使用oneof哪个更容易。

message UploadImageRequest{
        oneof test_oneof {
              Metadata meta = 2;
              bytes image = 1;
        }
}
message Metadata{
     AuthToken auth = 1;
     FileType image_format = 2;
}

rpc UploadImage(stream UploadImageRequest) returns (Ack); 

3:只需使用以下结构,在第一个块中发送元数据,其他块将有数据。你需要在代码中处理它。

syntax = 'proto3'

message UploadImageRequest{
    bytes message = 1;

}

rpc UploadImage(stream UploadImageRequest) returns (Ack); 

最后,对于身份验证,您可以使用标头而不是在消息中发送。

于 2017-11-20T05:32:58.243 回答