gRPC load testing
gRPC is a high-performance RPC framework using Protocol Buffers over HTTP/2. Load-testing gRPC services requires a client that understands the binary framing and can serialize protobuf messages. In MaxoPerf, the best supported approach for gRPC load testing is k6’s built-in k6/net/grpc module.
Before you start
Section titled “Before you start”- Read k6 scripts on Maxoperf — gRPC load testing uses k6 as the script engine.
- You need the
.protofile(s) for the service you are testing — k6 loads them at run time to understand the method signatures.
k6 gRPC module
Section titled “k6 gRPC module”k6 ships a k6/net/grpc module that lets you call gRPC unary and server-streaming methods:
import grpc from 'k6/net/grpc';import { check, sleep } from 'k6';
const client = new grpc.Client();
// Load the proto file — upload it as a Test asset alongside this scriptclient.load(['./proto'], 'product_service.proto');
export const options = { vus: 20, duration: '3m', thresholds: { grpc_req_duration: ['p(95)<200'], },};
export default function () { // Open connection (once per VU — k6 re-uses it across iterations) client.connect('grpc.api.example.com:443', { tls: true, });
// Unary call const response = client.invoke('product.v1.ProductService/GetProduct', { product_id: 'widget-01', });
check(response, { 'status OK': (r) => r && r.status === grpc.StatusOK, 'product name present': (r) => r.message && r.message.name !== '', });
sleep(1);
client.close();}The .proto file (product_service.proto) must be uploaded as a Test asset alongside the k6 script. The client.load(['./proto'], 'filename.proto') call tells k6 where to find it relative to the script root.
Proto file layout
Section titled “Proto file layout”If your proto imports other protos, upload them all as test assets and reference them via the import path array:
// Load multiple protos with a shared import pathclient.load( ['./proto', './proto/google'], // directories to search for imports 'product_service.proto', // entry proto file 'google/protobuf/timestamp.proto' // imported by product_service.proto);Upload your files as:
product_service.proto→ Test assetgoogle/protobuf/timestamp.proto→ Test asset
Wrapping the k6 gRPC script in Taurus YAML
Section titled “Wrapping the k6 gRPC script in Taurus YAML”To control concurrency and duration from a YAML without editing the script:
execution: - executor: k6 concurrency: 30 ramp-up: 1m hold-for: 10m scenario: grpc-product-load
scenarios: grpc-product-load: script: grpc_product_test.js # uploaded as a Test assetUpload the YAML as the Entrypoint and the k6 script + proto files as Test assets.
Server-streaming gRPC
Section titled “Server-streaming gRPC”k6’s gRPC module also supports server-streaming calls:
const stream = client.stream('product.v1.ProductService/WatchProduct', { product_id: 'widget-01',});
stream.on('data', (message) => { check(message, { 'update received': (m) => m.name !== '' });});
stream.on('end', () => { // stream closed by server});
stream.end();Reading gRPC results in MaxoPerf
Section titled “Reading gRPC results in MaxoPerf”gRPC results appear in the standard MaxoPerf run-detail view. Key metrics from the k6 gRPC module:
| k6 metric | MaxoPerf display |
|---|---|
grpc_req_duration | Latency chart (p50, p95, p99) |
grpc_req_failed | Error rate |
grpc_requests | Throughput (calls per second) |
Each distinct client.invoke(...) call appears as a separate label in the per-endpoint breakdown if you add a name tag:
const response = client.invoke( 'product.v1.ProductService/GetProduct', { product_id: 'widget-01' }, { tags: { name: 'GetProduct' } } // name appears in MaxoPerf per-endpoint panel);TLS and mTLS
Section titled “TLS and mTLS”For services requiring mutual TLS:
client.connect('grpc.api.example.com:443', { tls: true, // For mTLS: // tlsCert: open('client.crt', 'r'), // tlsKey: open('client.key', 'r'), // tlsCACert: open('ca.crt', 'r'),});Upload the certificate and key files as Test assets. Never commit real private keys to the uploaded test asset — use MaxoPerf secrets for keys and pass them via __ENV.CLIENT_KEY instead.
Do / don’t
Section titled “Do / don’t”Do:
- Upload the
.protofile as a Test asset — k6 needs it at run time to encode/decode protobuf messages. - Close the gRPC connection with
client.close()at the end of each VU iteration if you re-open it per iteration (or manage connection lifetime deliberately for connection-pool testing). - Use
thresholdsongrpc_req_durationin k6 options to automatically fail the run when latency exceeds your SLO.
Don’t:
- Inline proto definitions as strings in the k6 script — upload the
.protofile as a test asset and useclient.load(). - Forget to handle gRPC status codes in
check()— a status ofgrpc.StatusUnavailableunder load indicates the server is shedding requests.
Where to go next
Section titled “Where to go next”- k6 scripts on Maxoperf — k6 upload workflow.
- Taurus fundamentals — wrapping a k6 script in YAML for VU control.
- WebSocket load testing — next protocol page.
- Cookbook: failure criteria / pass-fail gates — fail runs automatically when gRPC latency exceeds thresholds.