Tutorials 12 November 2020

gRPC服务性能测试

Simon Aronsson(开发者推广大使)著,Ng Wai Foong 译

🎉 v0.29.0的新功能

v0.29.0包含许多特别有用的功能。 查看发布版本通知以了解更多详细信息!

📖你将会学到什么呢

  • 什么是gRPC。
  • gRPC与基于JSON的REST有何不同。
  • 使用k6创建并执行您的第一个gRPC性能测试。

概要

什么是gRPC?

gRPC是一个轻量级的开源的RPC框架,它最初是由谷歌(Google)开发的,2016年8月发布了1.0版本。此后,它得到了广泛的关注和采用。

与作为人类可读的文本传输的JSON相比,gRPC是二进制的,使其传输速度更快,而且它的结构更紧凑。在我们看到的基准测试中,事实证明gRPC比传统的基于JSON的REST快得多。

按照Auth0的基准,性能高达6倍,而其他基准,如Alex Pliutau的基准Ruwan Fernando的基准,则显示出高达10倍的改进。

对于分布式系统和涉及客户端与服务器之间大量通信的系统,这些改进意义重大,差异不仅在基准上明显,而且从终端用户的角度来看也非常明显

API类型

gRPC支持四种不同类型的RPC,一元(unary)、流媒体服务器(server streaming)、流媒体客户端(client streaming)和双向流(bi-directional streaming)。实际上,消息是使用相同的连接进行复用的,为了简单起见,下面的gRPC服务模型图中并未说明这个概念。

一元

一元的方式与常规函数调用相同,客户向服务器发送一个请求,服务器回复一个响应。

流媒体服务器

在流媒体服务器模式下,客户向服务器发送一个请求,服务器回复多个响应。

流媒体客户端

流媒体客户端模式与流媒体服务器模式相反,客户向服务器发送多个请求,服务器回复一个响应。

双向流

在双向流模式下,客户或服务器是可以互相发送多个信息。

proto定义

.proto文件中描述了gRPC相关的消息和服务,包含了Protocol buffers或protobuf和它的定义。然后,定义文件被用来生成可被发送方和接收方使用的代码,作为通过这些信息和服务进行交流的合同,由于gRPC使用的二进制格式缺乏自描述属性,所以需要这个文件来解释消息。

在本文中,我们将使用hello.proto定义,您可以通过k6的grpcbin网页下载它,有关如何构建自己的gRPC proto定义的详细信息,请参考官方gRPC文档.

// ./definitions/hello.proto
// based on https://grpc.io/docs/guides/concepts.html
syntax = "proto2";
package hello;
service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse);
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
}
message HelloRequest {
optional string greeting = 1;
}
message HelloResponse {
required string reply = 1;
}

引言

在k6 v0.29.0中,我们很高兴为gRPC通信引入一个原生客户端,在此版本中,我们为一元模式提供了良好的支持,如果其他模式对您有用,我们想进一步了解您的用例,这样以来我们可以为您优先考虑这些功能。

目前k6中使用原生客户端的gRPC API如下所示:

函数说明
Client.load(importPaths, ...protoFiles)为gRPC请求加载并解析给定的协议缓冲区定义。
Client.connect(address [,params])打开与gRPC服务器的连接
Client.invoke(url, request [,params])对服务进行一元RPC并返回响应
Client.close()关闭与gRPC服务的连接。

创建测试

gRPC模块是一个单独的包,可以从您的测试脚本中以k6/net/grpc的形式调用,在调用之前,我们必须创建一个客户端的实例,客户端实例化和.load操作仅在测试初始化时才能使用,例如直接在global范围内。

test.js
1import grpc from 'k6/net/grpc';
2
3const client = new grpc.Client();

接下来,我们将为要测系统加载一个.proto定义。在本文中,我们将使用k6 grpcbin,您可以使用自己的请求和响应服务,但请记住:“在测试时提供相应的.proto定义,.load()函数接收两个参数,第一个是proto文件路径数组,第二个是要加载的文件名。”

test.js
1import grpc from 'k6/net/grpc';
2
3const client = new grpc.Client();
4client.load(['definitions'], 'hello.proto');

完成后,我们将继续写我们的测试脚本。

test.js
1import grpc from 'k6/net/grpc';
2import { check, sleep } from 'k6';
3
4const client = new grpc.Client();
5client.load(['definitions'], 'hello.proto');
6
7export default () => {
8 client.connect('grpcbin.test.k6.io:9001', {
9 // plaintext: false
10 });
11
12 const data = { greeting: 'Bert' };
13 const response = client.invoke('hello.HelloService/SayHello', data);
14
15 check(response, {
16 'status is OK': (r) => r && r.status === grpc.StatusOK,
17 });
18
19 console.log(JSON.stringify(response.message));
20
21 client.close();
22 sleep(1);
23};

接下来,我们看一下以上的脚本以了解更多,我们首先调用.connect()函数连接到我们的测试系统,默认情况下,客户端会将plaintext参数设置为false,仅允许您使用加密的连接,如果您想连接到一个没有SSL/TLS的服务器,您只需将参数更改为true

继续创建要发送到grpc服务器的对象,至于SayHello,我们可以用greeting参数指定greeting地址。

接下来,我们使用proto文件中所述的语法<package>.<service>/<procedure>调用远程过程,该调用是同步进行的,默认超时时间为60000 ms(60秒),你可以通过在.connect()的config对象中加入timeout来改变超时时间,例如'2s'为2秒。

一旦我们收到服务器的响应,就意味着程序执行成功,k6的grpc模块包含了用于这种比较的常量,在此处列出

响应状态grpc.StatusOK相当于HTTP/1.1中的200 OK,使用它进行比较可确保调用成功完成。

然后,我们将记录响应消息,关闭客户端连接并进行sleep一秒钟。

运行测试

这个测试可以像其他测试一样运行,但你需要至少v0.29.0版本才能运行gRPC模块,运行以下命令以检查k6版本。

Bash
$ k6 version
k6 v0.29.0 ((devel), go1.15.3, darwin/amd64)

如果版本低于v0.29.0,请更新k6,您可以在此处找到安装说明。

完成后,继续运行我们的测试:

Bash
$ k6 run test.js
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | () |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: /Users/simme/code/grpc/test.js
output: -
scenarios: (100.00%) 1 scenario, 1 max VUs, 10m30s max duration (incl. graceful stop):
* default: 1 iterations for each of 1 VUs (maxDuration: 10m0s, gracefulStop: 30s)
INFO[0000] {"reply":"hello Bert"} source=console
running (00m01.4s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs 00m01.4s/10m0s 1/1 iters, 1 per VU
✓ status is OK
checks...............: 100.00% ✓ 10
data_received........: 3.0 kB 2.1 kB/s
data_sent............: 731 B 522 B/s
grpc_req_duration....: avg=48.44ms min=48.44ms med=48.44ms max=48.44ms p(90)=48.44ms p(95)=48.44ms
iteration_duration...: avg=1.37s min=1.37s med=1.37s max=1.37s p(90)=1.37s p(95)=1.37s
iterations...........: 1 0.714536/s
vus..................: 1 min=1 max=1
vus_max..............: 1 min=1 max=1

从输出结果来看,我们的脚本运行正常,服务器的响应为{"reply":"hello Bert"} Bert是我们在请求中发送的参数,我们也可以看到,check成功了,也就是说服务器响应了200 OK

摘要

在本文中,我们介绍了gRPC的一些基本原理以及如何使用它,我们还了解了k6 v0.29.0版本中引入的gRPC客户端。然后,我们创建了一个 测试脚本来展示这个功能。

本篇gRPC负载测试教程就到此结束了。感谢您的阅读!

参阅

< Back to all posts