菜单
文档breadcrumb arrow Grafana k6breadcrumb arrow 测试指南breadcrumb arrow gRPC 服务的性能测试
开源

gRPC 服务的性能测试

在本指南中,您将学习 gRPC 的基础知识以及如何使用 k6 编写 gRPC 性能测试。

开始之前

什么是 gRPC

gRPC 是一个轻量级的开源 RPC 框架。它最初由 Google 开发,1.0 版本于 2016 年 8 月发布。

与 JSON 这种以人类可读文本传输的方式相比,gRPC 是二进制格式,因此传输速度更快且更紧凑。对于频繁通信的分布式系统来说,这些改进累积起来会迅速显现,不仅在基准测试中显而易见,终端用户也能感受到差异。

API 类型

gRPC 支持四种不同类型的 RPC:一元、服务器流式、客户端流式和双向流式。实际上,消息是通过同一个连接进行多路复用的,但为了保持简单易懂,下面的 gRPC 服务模型图并未展示这一点。

一元 (Unary)

一元调用与常规函数调用工作方式相同:客户端发送单个请求到服务器,服务器回复单个响应。

Unary call

服务器流式 (Server streaming)

在服务器流式模式下,客户端向服务器发送单个请求,服务器则回复多个响应。

Server streaming

客户端流式 (Client streaming)

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

Client streaming

双向流式 (Bi-directional streaming)

在双向流式模式下,客户端和服务器都可以发送多个消息。

Bi-directional streaming

.proto 定义

用于 gRPC 的消息和服务在 .proto 文件中描述,这些文件包含 Protocol buffers(或简称 protobuf)定义。

然后使用定义文件生成代码,供发送方和接收方用作通过这些消息和服务进行通信的契约。由于 gRPC 使用的二进制格式缺乏自描述属性,这是发送方和接收方了解如何解释消息的唯一方式。

对于本指南,您可以使用 QuickPizza 演示应用程序 GitHub 存储库中提供的 quickpizza.proto 定义。有关如何构建您自己的 gRPC proto 定义的详细信息,请参阅官方 gRPC 文档

protobuf
syntax = "proto3";
option go_package = "pkg/grpc/quickpizza";
package quickpizza;

service GRPC {
    rpc Status(StatusRequest) returns (StatusResponse) {}
    rpc RatePizza(PizzaRatingRequest) returns (PizzaRatingResponse) {}
}

message StatusRequest {
}

message StatusResponse {
    bool ready = 1;
}

message PizzaRatingRequest {
    repeated string ingredients = 1;
    string dough = 2;
}

message PizzaRatingResponse {
    int32 stars_rating = 1;
}

使用 k6 编写 gRPC 性能测试

从 k6 v0.29.0 开始,您可以使用内置模块进行 gRPC 通信。您可以在 k6/net/grpc 模块文档中找到所有可用方法的详细信息。

创建测试

gRPC 模块是一个单独的模块,您可以从测试脚本中以 k6/net/grpc 方式访问它。在使用之前,您必须先创建一个客户端实例。客户端实例化以及 load() 函数仅在测试初始化期间可用,即直接在全局作用域中。

JavaScript
import grpc from 'k6/net/grpc';
import { check, sleep } from 'k6';

const client = new grpc.Client();

接下来,加载适用于被测系统的 .proto 定义。为了本文的目的,您可以使用 QuickPizza

注意

QuickPizza gRPC 服务 URL grpc-quickpizza.grafana.com:443 如果您在浏览器中访问它,会返回 464 HTTP 状态码。但是,您仍然可以在 k6 测试脚本中使用它,gRPC 功能将正常工作。

load() 函数接受两个参数,第一个参数是搜索 proto 文件的路径数组,第二个参数是要加载的文件名。

JavaScript
import grpc from 'k6/net/grpc';
import { check, sleep } from 'k6';

// Download quickpizza.proto for grpc-quickpizza.grafana.com, located at:
// https://raw.githubusercontent.com/grafana/quickpizza/refs/heads/main/proto/quickpizza.proto
// and put it in the same folder as this script.
const client = new grpc.Client();
client.load(['definitions'], 'quickpizza.proto');

完成后,添加调用 gRPC 服务的测试其余部分。

JavaScript
import grpc from 'k6/net/grpc';
import { check, sleep } from 'k6';

// Download quickpizza.proto for grpc-quickpizza.grafana.com, located at:
// https://raw.githubusercontent.com/grafana/quickpizza/refs/heads/main/proto/quickpizza.proto
// and put it in the same folder as this script.
const client = new grpc.Client();
client.load(null, 'quickpizza.proto');

export default () => {
  client.connect('grpc-quickpizza.grafana.com:443', {
    // plaintext: false
  });

  const data = { ingredients: ['Cheese'], dough: 'Thick' };
  const response = client.invoke('quickpizza.GRPC/RatePizza', data);

  check(response, {
    'status is OK': (r) => r && r.status === grpc.StatusOK,
  });

  console.log(JSON.stringify(response.message));

  client.close();
  sleep(1);
};

此测试脚本执行以下操作:

  1. 首先,它使用 .connect() 函数连接到被测系统。默认情况下,客户端将 plaintext 设置为 false,只允许您使用加密连接。如果出于任何原因需要连接到缺少 SSL/TLS 的服务器,只需将此设置切换为 true
  2. 然后脚本继续创建要发送到正在调用的远程过程的对象。在 RatePizza 的情况下,对象包含比萨的配料和面团类型。
  3. 接下来,它使用 proto 文件中描述的 <package>.<service>/<procedure> 语法调用远程过程。此调用是同步的,默认超时为 60000 毫秒(60 秒)。要更改超时,请将键 timeout 添加到 .connect() 的 config 对象中,并将持续时间作为值,例如 '2s' 表示 2 秒。
  4. k6 从服务器接收到响应后,脚本会检查以确保过程执行成功。gRPC 模块包含用于此比较的常量,这些常量列在此处。将响应状态与 grpc.StatusOK 进行比较(它与 HTTP/1.1 通信一样是 200 OK),确保调用成功完成。
  5. 脚本然后记录响应中的消息,关闭客户端连接,并暂停一秒。

运行测试

您可以使用 k6 run 命令像运行任何其他测试一样执行此测试:

bash
$ k6 run grpc-example.js

         /\      Grafana   /‾‾/
    /\  /  \     |\  __   /  /
   /  \/    \    | |/ /  /   ‾‾\
  /          \   |   (  |  (‾)  |
 / __________ \  |_|\_\  \_____/

     execution: local
        script: grpc-example.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] {"starsRating":3}                             source=console

     ✓ status is OK

     checks...............: 100.00% 1 out of 1
     data_received........: 4.1 kB  3.6 kB/s
     data_sent............: 762 B   656 B/s
     grpc_req_duration....: avg=33.75ms min=33.75ms med=33.75ms max=33.75ms p(90)=33.75ms p(95)=33.75ms
     iteration_duration...: avg=1.16s   min=1.16s   med=1.16s   max=1.16s   p(90)=1.16s   p(95)=1.16s
     iterations...........: 1       0.860427/s
     vus..................: 1       min=1      max=1
     vus_max..............: 1       min=1      max=1


running (00m01.2s), 0/1 VUs, 1 complete and 0 interrupted iterations
default ✓ [======================================] 1 VUs  00m01.2s/10m0s  1/1 iters, 1 per VU

从输出中,您可以检查脚本是否正常工作,以及服务器是否确实回复了请求体中提供的比萨类型的评分。您还可以看到 check 成功了,这意味着服务器回复了 200 OK

总结

在本文中,您了解了一些 gRPC 的基础知识及其工作原理。您还查看了 k6 gRPC 客户端并创建了一个有效的测试脚本来演示其功能。

附加资源