gRPC + 图片上传
gRPC + Image Upload
我想创建一个简单的 gRPC
端点,用户可以上传 his/her 图片。协议缓冲区声明如下:
message UploadImageRequest {
AuthToken auth = 1;
// An enum with either JPG or PNG
FileType image_format = 2;
// Image file as bytes
bytes image = 3;
}
无论gRPC文档中的警告如何,这种上传图片(和接收图片)的方法仍然可以吗?
如果不是,使用标准格式上传图片并存储图像文件位置是否是更好的方法(标准)?
对于大型二进制传输,标准方法是分块。分块有两个目的:
- 减少处理每条消息所需的最大内存量
- 提供恢复部分上传的边界。
对于您的 use-case #2 可能不是很有必要。
在 gRPC 中,client-streaming 调用允许相当自然的分块,因为它具有流量控制、流水线,并且易于在客户端和服务器代码中维护上下文。如果您关心部分上传的恢复,那么 bidirectional-streaming 效果很好,因为服务器可以响应客户端可以用来恢复的进度确认。
使用单独的 RPC 进行分块也是可能的,但更复杂。当负载平衡时,后端可能需要在每个块中与其他后端协调。如果您连续上传块,那么网络延迟会降低上传速度,因为您大部分时间都在等待从服务器接收响应。然后,您要么必须并行上传(但有多少并行上传?),要么增加块大小。但是增加块大小会增加处理每个块所需的内存,并增加恢复失败上传的粒度。并行上传还需要服务器处理 out-of-order 次上传。
问题中提供的解决方案不适用于大文件。它只适用于较小的图像尺寸。
更好的标准方法是使用分块。 grpc 支持内置流式传输。因此发送块相当容易
syntax = 'proto3'
message UploadImageRequest{
bytes image = 1;
}
rpc UploadImage(stream UploadImageRequest) returns (Ack);
通过上面的方式我们可以使用streaming来分块
对于分块,所有语言都提供了自己的方法来根据块大小对文件进行分块。
注意事项:
您需要处理分块逻辑,流式传输有助于自然发送。
如果你想发送元数据也有三种方法。
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);
最后,对于身份验证,您可以使用 headers 而不是在消息中发送。
我想创建一个简单的 gRPC
端点,用户可以上传 his/her 图片。协议缓冲区声明如下:
message UploadImageRequest {
AuthToken auth = 1;
// An enum with either JPG or PNG
FileType image_format = 2;
// Image file as bytes
bytes image = 3;
}
无论gRPC文档中的警告如何,这种上传图片(和接收图片)的方法仍然可以吗?
如果不是,使用标准格式上传图片并存储图像文件位置是否是更好的方法(标准)?
对于大型二进制传输,标准方法是分块。分块有两个目的:
- 减少处理每条消息所需的最大内存量
- 提供恢复部分上传的边界。
对于您的 use-case #2 可能不是很有必要。
在 gRPC 中,client-streaming 调用允许相当自然的分块,因为它具有流量控制、流水线,并且易于在客户端和服务器代码中维护上下文。如果您关心部分上传的恢复,那么 bidirectional-streaming 效果很好,因为服务器可以响应客户端可以用来恢复的进度确认。
使用单独的 RPC 进行分块也是可能的,但更复杂。当负载平衡时,后端可能需要在每个块中与其他后端协调。如果您连续上传块,那么网络延迟会降低上传速度,因为您大部分时间都在等待从服务器接收响应。然后,您要么必须并行上传(但有多少并行上传?),要么增加块大小。但是增加块大小会增加处理每个块所需的内存,并增加恢复失败上传的粒度。并行上传还需要服务器处理 out-of-order 次上传。
问题中提供的解决方案不适用于大文件。它只适用于较小的图像尺寸。 更好的标准方法是使用分块。 grpc 支持内置流式传输。因此发送块相当容易
syntax = 'proto3'
message UploadImageRequest{
bytes image = 1;
}
rpc UploadImage(stream UploadImageRequest) returns (Ack);
通过上面的方式我们可以使用streaming来分块
对于分块,所有语言都提供了自己的方法来根据块大小对文件进行分块。
注意事项:
您需要处理分块逻辑,流式传输有助于自然发送。 如果你想发送元数据也有三种方法。
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);
最后,对于身份验证,您可以使用 headers 而不是在消息中发送。