J.F*_*.F. 10 rest protocol-buffers grpc grpc-node
我正在 Node.JS 中实现的服务之间进行负载测试,同一台机器上的两个服务都通过localhost
.
有 REST 和 gRPC 客户端和服务器文件。主要目标是证明 gRPC 比 HTTP 调用更快,因为使用 HTTP/2、使用比编码/解码 JSON 更高效的协议缓冲区......
但在我的测试中(发送整数数组)gRPC 慢得多。
对于这两种实现来说,代码都非常简单,我有一个辅助类来生成大小为(以 MB 为单位)的对象:0.125、0.25、0.5、1、2、5、20。REST 和 gRPC 服务器使用此辅助类,因此对象发送是一样的。
有效负载中发送的对象如下所示:
{
message: "Hello world",
array: []
}
Run Code Online (Sandbox Code Playgroud)
数组中填充数字,直到达到所需的大小。
我的 .proto 是这样的:
syntax = "proto3";
service ExampleService {
rpc GetExample (Size) returns (Example) {}
}
message Size {
int32 size = 1;
}
message Example {
string message = 1;
repeated int32 array = 2;
}
Run Code Online (Sandbox Code Playgroud)
另外,我运行的应用程序仅测量一次调用,不创建循环并查找平均值,也不用回调处理测量时间。所以我运行该应用程序 10 次并计算平均值。
休息服务器:
app.get('/:size',(req,res) => {
const size = req.params.size
res.status(200).send(objects[size])
})
Run Code Online (Sandbox Code Playgroud)
休息客户端:
const start = performance.now()
const response = await axios.get(`http://localhost:8080/${size}`)
const end = performance.now()
Run Code Online (Sandbox Code Playgroud)
gRPC 服务器:
getExample:(call, callback) => {
callback(null, objects.objects[call.request.size])
}
Run Code Online (Sandbox Code Playgroud)
和 gRPC 客户端:
const start = performance.now()
client.getExample({ size: size }, (error, response) => {
const end = performance.now()
})
Run Code Online (Sandbox Code Playgroud)
为了更有效地做事,我尝试过:
let server = new grpc.Server({
'grpc.default_compression_level': 3, // (1->Low -- 3->High)
});
Run Code Online (Sandbox Code Playgroud)
我知道我可以用来streaming
获取数据并迭代数组,但我想证明这两种方法中的“相同的调用”。
而且差别这么大。
我看到的另一件事是,使用 REST 方式的时间更加“线性”,时间之间的差异很小,但使用 gRPC 一次调用发送 2MB 可能需要 220 毫秒,而下一个调用可能需要 500 毫秒。
这是最终的比较,您可以看到差异相当大。
数据:
大小(MB) | 休息(毫秒) | gRPC(毫秒) |
---|---|---|
0,125 | 37.98976998329162 | 35.5489800453186 |
0,25 | 40.03781998157501 | 46.077759981155396 |
0,5 | 51.35283002853394 | 59.37109994888306 |
1 | 63.4725800037384 | 166.7616500457128 |
2 | 95.76031665007274 | 394.2442199707031 |
5 | 261.9365399837494 | 804.1371199131012 |
20 | 713.1867599964141 | 5492.330539941788 |
但我想...也许数组字段无法以有效的方式解码,也许是整数,这对于 JSON 来说并不重...我不知道,所以我将尝试发送一个绳子,一根非常巨大的绳子。
所以我的原型文件现在看起来像这样:
syntax = "proto3";
service ExampleService {
rpc GetExample (Size) returns (Example) {}
}
message Size {
int32 size = 1;
}
message Example {
string message = 1;
string array = 2;
}
Run Code Online (Sandbox Code Playgroud)
现在发送的对象是这样的:
{
message: "Hello world",
array: "text to reach the desired MB"
}
Run Code Online (Sandbox Code Playgroud)
结果差异很大,现在 gRPC 效率更高了。
数据:
大小(MB) | 休息(毫秒) | gRPC(毫秒) |
---|---|---|
0,125 | 30.672580003738403 | 25.028959941864013 |
0,25 | 33.568540048599246 | 25.366739988327026 |
0,5 | 37.19938006401062 | 27.539460039138795 |
1 | 46.4020166794459 | 28.798949996630352 |
2 | 57.50188330809275 | 35.45066670576731 |
5 | 107.39933327833812 | 48.90079998970032 |
20 | 313.4138665994008 | 136.4138500293096 |
问题是:那么,为什么发送整数数组不如发送字符串那么有效?protobuf 是对数组进行编码/解码的方式吗?是不是高效发送repeated
值?与语言(JS)有关吗?
gRPC(实际上是 protobufs)在您的示例中无法很好地扩展的原因是,字段的每个条目repeated
都会导致 protobuf 需要解码单独的字段,并且存在与此相关的开销。您可以在此处的文档中查看有关重复字段编码的更多详细信息。您正在使用proto3
,因此至少您不需要指定该[packed=true]
选项,尽管如果您使用 ,这会有所帮助proto2
。
string
切换到or字段的速度如此之快的原因bytes
是,该字段只有一个恒定的解码成本,它不会随着该字段中编码的数据量而扩展(尽管不确定 JS,这可能需要创建数据的副本,但显然这仍然比实际解析数据快得多)。只需确保您的协议定义了字段中数据的格式/字节顺序:-)
在更高层次上回答你的问题,在单个 API 调用中发送多个兆字节通常并不是一个令人惊奇的想法 - 它会长时间占用服务器和客户端上的线程,这迫使你使用多线程或异步代码来获得合理的性能。(诚然,这可能不是什么问题,因为您习惯在 Node 上编写异步内容,但服务器上仍然只有这么多 CPU 需要消耗。)
\n根据您实际尝试执行的操作,常见模式可能是将数据写入共享存储系统(S3 等)中的文件并将文件名传递给其他服务,然后该服务可以在以下情况下下载它:它实际上是需要的。
\n 归档时间: |
|
查看次数: |
6506 次 |
最近记录: |