gRPC API 架构建议
gRPC API Architecture advice
现在我正在 Python 中开发微服务应用程序。
基本上就像GUI -(gRPC)-> Server -(gRPC?)-> Workers
。
GUI 对服务器进行了各种调用,例如所有配置都存储在服务器上。为了性能和简单性,我决定使用#gRPC 作为 API 传输。
我已经实现了客户端和服务器类,现在我有一个两难选择:
- Performance best practices 告诉我们
Always re-use stubs and channels when possible.
出于这个原因,我可以制作通用的 rpc 通道,如:
UniMsg {
int32 id = 1;
string name = 2;
bytes data = 3;
}
在客户端打包数据
(在某些情况下,不可能在 proto
中创建完整的数据模式。例如配置写成 JSON 以将其传输到 GUI 我要么必须在 proto
中实现所有配置字段,这对我来说似乎有点头疼,或者我可以将 JSON 编码为字节并以这种方式发送。它不会是透明的,但它允许修改配置结构而不需要 modify/compile proto
。而且发送编码字节的性能似乎明显更好,我不需要 serialize/deserialize 它。)
,标记为name
,根据name
字段在服务器端解包处理。这会重用 stub
并将所需的 stubs
数量保持为一个。
唯一的缺点是它有点不是 RPC,它更像是 data bridge
.
- 我可以创建多个
services
,但它们确实需要单独的 stubs
和完整的数据描述。从 API 描述的角度来看这很好(即为了进一步扩展,无需查看 server/client “黑匣子”,您只需查看 proto 文件并根据描述的规则发送消息),但效果较差性能方面,因为我们使用新的 stubs
.
syntax = "proto3";
message FilesAndFilters {
repeated string file = 1;
repeated string ext = 2;
}
message ResultList {
repeated string file = 1;
}
// <ServiceName>
service FList {
// This is common method, it is described on server and called via stub on client.
// <MethodName>(<ParamRequest>)
rpc StreamFiles(FilesAndFilters) returns (ResultList);
}
message Settings {
int32 profile = 1;
bytes config = 2;
}
service CCConfig {
rpc StreamConfig(Settings) returns (Settings);
}
向 gRPC 专家提出的问题:什么是最佳的中间立场来保持一切透明和不言自明并保持性能。
请注意,在某些时候可能会出现争论,即我的应用程序负载很低,性能损失可以忽略不计。我的目标是在将技术投入生产之前分享经验并了解所有怪癖,因此可以在高负载系统上使用相同的方法。
谢谢。
我认为您可能误解了“尽可能 re-use 存根和频道”这句话。本指南旨在避免重复拆卸和重新创建客户端通道,而不是将所有 RPC 服务合并为一个。
简而言之,选择选项 #2。拥有多种服务是惯用的,也是值得鼓励的。
现在我正在 Python 中开发微服务应用程序。
基本上就像GUI -(gRPC)-> Server -(gRPC?)-> Workers
。
GUI 对服务器进行了各种调用,例如所有配置都存储在服务器上。为了性能和简单性,我决定使用#gRPC 作为 API 传输。
我已经实现了客户端和服务器类,现在我有一个两难选择:
- Performance best practices 告诉我们
Always re-use stubs and channels when possible.
出于这个原因,我可以制作通用的 rpc 通道,如:
UniMsg {
int32 id = 1;
string name = 2;
bytes data = 3;
}
在客户端打包数据
(在某些情况下,不可能在 proto
中创建完整的数据模式。例如配置写成 JSON 以将其传输到 GUI 我要么必须在 proto
中实现所有配置字段,这对我来说似乎有点头疼,或者我可以将 JSON 编码为字节并以这种方式发送。它不会是透明的,但它允许修改配置结构而不需要 modify/compile proto
。而且发送编码字节的性能似乎明显更好,我不需要 serialize/deserialize 它。)
,标记为name
,根据name
字段在服务器端解包处理。这会重用 stub
并将所需的 stubs
数量保持为一个。
唯一的缺点是它有点不是 RPC,它更像是 data bridge
.
- 我可以创建多个
services
,但它们确实需要单独的stubs
和完整的数据描述。从 API 描述的角度来看这很好(即为了进一步扩展,无需查看 server/client “黑匣子”,您只需查看 proto 文件并根据描述的规则发送消息),但效果较差性能方面,因为我们使用新的stubs
.
syntax = "proto3";
message FilesAndFilters {
repeated string file = 1;
repeated string ext = 2;
}
message ResultList {
repeated string file = 1;
}
// <ServiceName>
service FList {
// This is common method, it is described on server and called via stub on client.
// <MethodName>(<ParamRequest>)
rpc StreamFiles(FilesAndFilters) returns (ResultList);
}
message Settings {
int32 profile = 1;
bytes config = 2;
}
service CCConfig {
rpc StreamConfig(Settings) returns (Settings);
}
向 gRPC 专家提出的问题:什么是最佳的中间立场来保持一切透明和不言自明并保持性能。
请注意,在某些时候可能会出现争论,即我的应用程序负载很低,性能损失可以忽略不计。我的目标是在将技术投入生产之前分享经验并了解所有怪癖,因此可以在高负载系统上使用相同的方法。
谢谢。
我认为您可能误解了“尽可能 re-use 存根和频道”这句话。本指南旨在避免重复拆卸和重新创建客户端通道,而不是将所有 RPC 服务合并为一个。
简而言之,选择选项 #2。拥有多种服务是惯用的,也是值得鼓励的。