Load test gRPC services with Gatling: configure channels, TLS, headers, unary and streaming methods, and response checks across Java, Kotlin, and Scala.
Use this file to discover all available pages before exploring further.
Gatling’s gRPC support lets you load test high-performance RPC services built on gRPC, Google’s open-source framework that runs over HTTP/2. Whether your service exposes unary calls, server-streaming, client-streaming, or bidirectional-streaming methods, Gatling can model each interaction pattern faithfully—measuring latency, throughput, and error rates at scale. Under the hood, Gatling uses gRPC-Java and requires Protocol Buffers descriptors (generated from .proto files) to serialize messages.
The Gatling gRPC component is distributed under the Gatling Enterprise Component License. When run with the Community Edition, tests are limited to 5 users and 5 minutes. Unlimited usage requires Gatling Enterprise.
libraryDependencies += "io.gatling" %% "gatling-grpc" % gatlingVersion % Test
2
Configure Protobuf code generation
gRPC uses Protocol Buffers to serialize messages. You need to generate method descriptor and message classes from your .proto files.Scala (sbt) — ScalaPB:
A complete demo project is available for Java (Maven/Gradle), Kotlin (Maven/Gradle), Scala (sbt), JavaScript, and TypeScript, including a runnable demo server.
Use the grpc object to configure the gRPC protocol. In modern Gatling gRPC, connection settings are expressed as server configurations rather than being applied directly to the protocol object.
A server configuration defines target address and connection options for one or more gRPC servers. When multiple configurations are defined, the first one is used as the default.
Bundled with Gatling gRPC; not a standard gRPC policy.
Custom Policy
grpc.forAddress("host", 50051) .useCustomLoadBalancingPolicy("my-policy")// or with JSON config:grpc.forAddress("host", 50051) .useCustomLoadBalancingPolicy("my-policy", jsonConfig)
The client sends one message; the server streams back multiple responses. Use awaitStreamEnd to wait for the server to close the stream.Assign the stream to a variable so you can reference it in multiple exec steps:
// Define the stream (checks are attached before send)GrpcServerStreamingServiceBuilder<StreamRequest, ExampleResponse> stream = grpc("server stream") .serverStream(ExampleServiceGrpc.getServerStreamMethod()) .check(response(ExampleResponse::getMessage).saveAs("lastMessage"));// Execute: send opens the stream; awaitStreamEnd waits for server closeexec( stream.send(StreamRequest.newBuilder().build()), stream.awaitStreamEnd())
When running multiple concurrent server streams per user, assign explicit names:
The client sends multiple messages; the server replies once after the client half-closes.
// Open the streamgrpc("client stream").clientStream(method).start()// Send messagesgrpc("send 1").clientStream(method).send(msg1)grpc("send 2").clientStream(method).send(msg2)// Signal end of client messages and wait for server responsegrpc("half close").clientStream(method).halfClose()grpc("await end").clientStream(method).awaitStreamEnd()
For server and bidirectional streams, buffer and process messages that were not matched by checks. Call processUnmatchedMessages on a pre-created stream variable:
GrpcBidirectionalStreamingServiceBuilder<Req, Resp> stream = grpc("name").bidiStream(method);// Process unmatched messages at a point during the stream lifecycleexec(stream.processUnmatchedMessages((messages, session) -> { // messages sorted oldest-first (GrpcInboundMessage instances) return session.set("messages", messages);}))// Or combine with awaitStreamEnd to process remaining messages when the stream endsexec(stream.awaitStreamEndAndProcessUnmatchedMessages((messages, session) -> session.set("messages", messages)))
The buffer resets when an outbound message is sent or processUnmatchedMessages is called. Enable buffering via unmatchedInboundMessageBufferSize on the protocol.
// Status code.check(statusCode().is(Status.Code.OK)).check(statusCode().not(Status.Code.UNAVAILABLE))// Status description (optional).check(statusDescription().is("expected description")).check(statusDescription().isNull())// Status cause (optional Throwable).check(statusCause().isNull())
// ASCII header by name.check(asciiHeader("content-type").is("application/grpc"))// Binary header.check(binaryHeader(myBinaryKey).is(expectedBytes))// Multi-valued header as a list.check(asciiHeader("x-multi").isEL("#{expectedList}"))// Trailers work identically.check(asciiTrailer("grpc-status-message").exists())
Binary header and trailer keys must end with the suffix -bin as required by the gRPC specification.
// Extract a field from the response message.check(response(ExampleResponse::getResultCode).is(200))// Save a field for use in later requests.check(response(ExampleResponse::getToken).saveAs("authToken"))
When using Scala or Java lambdas, the parameter type cannot be inferred and must be specified explicitly.
Checks are defined before send (at stream instantiation). Message checks apply to every received message; status, header, and trailer checks apply once at stream end.
It is not currently possible to apply different checks to different incoming messages in the same stream. Using saveAs in a message check will overwrite previously saved values each time a message is received.