gRPC Methods
Learn how to use each method offered by the Gatling gRPC DSL
Summary
With gRPC, four types of methods can be defined: unary, server streaming, client streaming and bidirectional streaming. Different Gatling DSL methods can be used depending on the type of the gRPC method.
Unary | Server Stream | Client Stream | Bidirectional Stream | |
---|---|---|---|---|
Instantiate | unary |
serverStream |
clientStream |
bidiStream |
Add request headers | asciiHeader(s) binaryHeader(s) header |
asciiHeader(s) binaryHeader(s) header |
asciiHeader(s) binaryHeader(s) header |
asciiHeader(s) binaryHeader(s) header |
Add call credentials | callCredentials |
callCredentials |
callCredentials |
callCredentials |
Add deadline | deadlineAfter |
deadlineAfter |
deadlineAfter |
deadlineAfter |
Add checks | check |
check |
check |
check |
Response time policy | ❌ | messageResponseTimePolicy |
❌ | messageResponseTimePolicy |
Open stream | ❌ | implied by send |
start |
start |
Send a message | send |
send |
send |
send |
Half-close stream | ❌ | implied by send |
halfClose |
halfClose |
Wait for stream end | ❌ | awaitStreamEnd |
awaitStreamEnd |
awaitStreamEnd |
Cancel stream | ❌ | cancel |
cancel |
cancel |
gRPC method descriptor
The Gatling gRPC DSL will need a method descriptor, of type io.grpc.MethodDescriptor
, to define each gRPC method used.
The most common use case is to use generated code from a .proto
specification file which describes the gRPC service, but the method descriptor could also be constructed by hand.
In all code examples on this page, we assume a method descriptor defined by Java code similar to this:
public final class ExampleServiceGrpc {
public static MethodDescriptor<ExampleRequest, ExampleResponse> getExampleMethod() {
// generated method descriptor code here
}
}
Instantiate a gRPC request
Unary method calls
For unary gRPC methods, Gatling gRPC requests are declared with the unary
keyword.
grpc(requestName)
is the entrypoint for any gRPC request with the Gatling gRPC DSL. unary(methodDescriptor)
then
takes a method descriptor describing the gRPC method to call (which must describe a
unary method).
// with a static value
grpc("request name").unary(ExampleServiceGrpc.getExampleMethod());
// with a Gatling EL string
grpc("#{requestName}").unary(ExampleServiceGrpc.getExampleMethod());
// with a function
grpc(session -> session.getString("requestName")).unary(ExampleServiceGrpc.getExampleMethod());
// with a static value
grpc("request name").unary(ExampleServiceGrpc.getExampleMethod())
// with a Gatling EL string
grpc("#{requestName}").unary(ExampleServiceGrpc.getExampleMethod())
// with a function
grpc { session -> session.getString("requestName") }.unary(ExampleServiceGrpc.getExampleMethod())
// with a static value
grpc("request name").unary(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a Gatling EL string
grpc("#{requestName}").unary(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a function
grpc(session => session("requestName").as[String]).unary(ExampleServiceGrpc.METHOD_EXAMPLE)
When you send
a message, Gatling gRPC will automatically handle the client-side lifecycle of the underlying gRPC
stream (open a stream, send a single message, half-close the stream) and wait for the server to respond and close the
stream.
ScenarioBuilder scn = scenario("scenario name").exec(
// Sends a request and awaits a response, similarly to regular HTTP requests
grpc("request name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(new RequestMessage("hello"))
);
val scn = scenario("scenario name").exec(
// Sends a request and awaits a response, similarly to regular HTTP requests
grpc("request name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(RequestMessage ("hello"))
)
val scn = scenario("scenario name").exec(
// Sends a request and awaits a response, similarly to regular HTTP requests
grpc("request name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(RequestMessage("hello"))
)
Streaming method calls
For streaming gRPC methods, Gatling gRPC requests are declared with the serverStream
, clientStream
, and bidiStream
keyword. Including one of them in a scenario creates a gRPC stream which may stay open for a long time, and allows you
to perform several actions on the same stream at various times during the scenario’s execution.
Server stream
grpc(requestName)
is the entrypoint for any gRPC request with the Gatling gRPC DSL. serverStream(methodDescriptor)
then
takes a method descriptor describing the gRPC method to call (which must describe a
server streaming method).
// with a static value
grpc("request name").serverStream(ExampleServiceGrpc.getExampleMethod());
// with a Gatling EL string
grpc("#{requestName}").serverStream(ExampleServiceGrpc.getExampleMethod());
// with a function
grpc(session -> session.getString("requestName")).serverStream(ExampleServiceGrpc.getExampleMethod());
// with a static value
grpc("request name").serverStream(ExampleServiceGrpc.getExampleMethod())
// with a Gatling EL string
grpc("#{requestName}").serverStream(ExampleServiceGrpc.getExampleMethod())
// with a function
grpc { session -> session.getString("requestName") }.serverStream(ExampleServiceGrpc.getExampleMethod())
// with a static value
grpc("request name").serverStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a Gatling EL string
grpc("#{requestName}").serverStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a function
grpc(session => session("requestName").as[String]).serverStream(ExampleServiceGrpc.METHOD_EXAMPLE)
The typical lifecycle of a server stream consists of:
- Sending a single message with the
send
method (this will also half-close the stream, signaling that the client will not send any more messages) - Waiting until the stream gets closed by the server with the
awaitStreamEnd
method
GrpcServerStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("request name").serverStream(ExampleServiceGrpc.getExampleMethod());
ScenarioBuilder scn = scenario("scenario name").exec(
stream.send(message),
stream.awaitStreamEnd()
);
val stream = grpc("request name").serverStream(ExampleServiceGrpc.getExampleMethod())
val scn = scenario("scenario name").exec(
stream.send(message),
stream.awaitStreamEnd()
)
val stream = grpc("request name").serverStream(ExampleServiceGrpc.METHOD_EXAMPLE)
val scn = scenario("scenario name").exec(
stream.send(message),
stream.awaitStreamEnd
)
If several server streams are opened concurrently by a virtual user, they must be given explicit stream names to differentiate them:
GrpcServerStreamingServiceBuilder<RequestMessage, ResponseMessage> stream1 =
// specify streamName initially
grpc("request name").serverStream(ExampleServiceGrpc.getExampleMethod(), "first-stream");
GrpcServerStreamingServiceBuilder<RequestMessage, ResponseMessage> stream2 =
grpc("request name")
.serverStream(ExampleServiceGrpc.getExampleMethod())
// or use the streamName method
.streamName("second-stream");
exec(
stream1.send(message),
stream2.send(message)
);
// both streams are concurrently open at this point
val stream1 = grpc("request name")
// specify streamName initially
.serverStream(ExampleServiceGrpc.getExampleMethod(), "first-stream")
val stream2 = grpc("request name")
.serverStream(ExampleServiceGrpc.getExampleMethod())
// or use the streamName method
.streamName("second-stream")
exec(
stream1.send(message),
stream2.send(message)
)
// both streams are concurrently open at this point
val stream1 = grpc("request name")
// specify streamName initially
.serverStream(ExampleServiceGrpc.METHOD_EXAMPLE, "first-stream")
val stream2 = grpc("request name")
.serverStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// or use the streamName method
.streamName("second-stream")
exec(
stream1.send(message),
stream2.send(message)
)
// both streams are concurrently open at this point
Client stream
grpc(requestName)
is the entrypoint for any gRPC request with the Gatling gRPC DSL. clientStream(methodDescriptor)
then
takes a method descriptor describing the gRPC method to call (which must describe a
client streaming method).
// with a static value
grpc("request name").clientStream(ExampleServiceGrpc.getExampleMethod());
// with a Gatling EL string
grpc("#{requestName}").clientStream(ExampleServiceGrpc.getExampleMethod());
// with a function
grpc(session -> session.getString("requestName")).clientStream(ExampleServiceGrpc.getExampleMethod());
// with a static value
grpc("request name").clientStream(ExampleServiceGrpc.getExampleMethod())
// with a Gatling EL string
grpc("#{requestName}").clientStream(ExampleServiceGrpc.getExampleMethod())
// with a function
grpc { session -> session.getString("requestName") }.clientStream(ExampleServiceGrpc.getExampleMethod())
// with a static value
grpc("request name").clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a Gatling EL string
grpc("#{requestName}").clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a function
grpc(session => session("requestName").as[String]).clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
The typical lifecycle of a client stream consists of:
- Opening the stream with the
start
method - Sending messages with the
send
method - Half-closing the stream with the
halfClose
method when done sending messages - Waiting until the stream gets closed by the server with the
awaitStreamEnd
method
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("request name").clientStream(ExampleServiceGrpc.getExampleMethod());
ScenarioBuilder scn = scenario("scenario name").exec(
stream.start(),
stream.send(message1),
stream.send(message2),
stream.halfClose(),
stream.awaitStreamEnd()
);
val stream = grpc("request name").clientStream(ExampleServiceGrpc.getExampleMethod())
val scn = scenario("scenario name").exec(
stream.start(),
stream.send(message1),
stream.send(message2),
stream.halfClose(),
stream.awaitStreamEnd()
)
val stream = grpc("request name").clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
val scn = scenario("scenario name").exec(
stream.start,
stream.send(message1),
stream.send(message2),
stream.halfClose,
stream.awaitStreamEnd
)
If several client streams are opened concurrently by a virtual user, they must be given explicit stream names to differentiate them:
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream1 =
// specify streamName initially
grpc("request name").clientStream(ExampleServiceGrpc.getExampleMethod(), "first-stream");
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream2 =
grpc("request name")
.clientStream(ExampleServiceGrpc.getExampleMethod())
// or use the streamName method
.streamName("second-stream");
exec(
stream1.start(),
stream2.start()
);
// both streams are concurrently open at this point
val stream1 = grpc("request name")
// specify streamName initially
.clientStream(ExampleServiceGrpc.getExampleMethod(), "first-stream")
val stream2 = grpc("request name")
.clientStream(ExampleServiceGrpc.getExampleMethod())
// or use the streamName method
.streamName("second-stream")
exec(
stream1.start(),
stream2.start()
)
// both streams are concurrently open at this point
val stream1 = grpc("request name")
// specify streamName initially
.clientStream(ExampleServiceGrpc.METHOD_EXAMPLE, "first-stream")
val stream2 = grpc("request name")
.clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// or use the streamName method
.streamName("second-stream")
exec(
stream1.start,
stream2.start
)
// both streams are concurrently open at this point
Bidirectional stream
grpc(requestName)
is the entrypoint for any gRPC request with the Gatling gRPC DSL. bidiStream(methodDescriptor)
then
takes a method descriptor describing the gRPC method to call (which must describe a
bidirectional streaming method).
// with a static value
grpc("request name").bidiStream(ExampleServiceGrpc.getExampleMethod());
// with a Gatling EL string
grpc("#{requestName}").bidiStream(ExampleServiceGrpc.getExampleMethod());
// with a function
grpc(session -> session.getString("requestName")).bidiStream(ExampleServiceGrpc.getExampleMethod());
// with a static value
grpc("request name").bidiStream(ExampleServiceGrpc.getExampleMethod())
// with a Gatling EL string
grpc("#{requestName}").bidiStream(ExampleServiceGrpc.getExampleMethod())
// with a function
grpc { session -> session.getString("requestName") }.bidiStream(ExampleServiceGrpc.getExampleMethod())
// with a static value
grpc("request name").bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a Gatling EL string
grpc("#{requestName}").bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// with a function
grpc(session => session("requestName").as[String]).bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
The typical lifecycle of a bidirectional stream consists of:
- Opening the stream with the
start
method - Sending messages with the
send
method - Half-closing the stream with the
halfClose
method when done sending messages - Waiting until the stream gets closed by the server with the
awaitStreamEnd
method
GrpcBidirectionalStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("request name").bidiStream(ExampleServiceGrpc.getExampleMethod());
ScenarioBuilder scn = scenario("scenario name").exec(
stream.start(),
stream.send(message1),
stream.send(message2),
stream.halfClose(),
stream.awaitStreamEnd()
);
val stream = grpc("request name").bidiStream(ExampleServiceGrpc.getExampleMethod())
val scn = scenario("scenario name").exec(
stream.start(),
stream.send(message1),
stream.send(message2),
stream.halfClose(),
stream.awaitStreamEnd()
)
val stream = grpc("request name").bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
val scn = scenario("scenario name").exec(
stream.start,
stream.send(message1),
stream.send(message2),
stream.halfClose,
stream.awaitStreamEnd
)
If several bidirectional streams are opened concurrently by a virtual user, they must be given explicit stream names to differentiate them:
GrpcBidirectionalStreamingServiceBuilder<RequestMessage, ResponseMessage> stream1 =
// specify streamName initially
grpc("request name").bidiStream(ExampleServiceGrpc.getExampleMethod(), "first-stream");
GrpcBidirectionalStreamingServiceBuilder<RequestMessage, ResponseMessage> stream2 =
grpc("request name")
.bidiStream(ExampleServiceGrpc.getExampleMethod())
// or use the streamName method
.streamName("second-stream");
exec(
stream1.start(),
stream2.start()
);
// both streams are concurrently open at this point
val stream1 = grpc("request name")
// specify streamName initially
.bidiStream(ExampleServiceGrpc.getExampleMethod(), "first-stream")
val stream2 = grpc("request name")
.bidiStream(ExampleServiceGrpc.getExampleMethod())
// or use the streamName method
.streamName("second-stream")
exec(
stream1.start(),
stream2.start()
)
// both streams are concurrently open at this point
val stream1 = grpc("request name")
// specify streamName initially
.bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE, "first-stream")
val stream2 = grpc("request name")
.bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// or use the streamName method
.streamName("second-stream")
exec(
stream1.start,
stream2.start
)
// both streams are concurrently open at this point
Methods reference
Add request headers
unary serverStream clientStream bidiStream
You can easily add ASCII format request headers (they will use the standard ASCII marshaller,
io.grpc.Metadata#ASCII_STRING_MARSHALLER
):
// Extracting a map of headers allows you to reuse these in several requests
Map<String, String> sentHeaders = new HashMap<>();
sentHeaders.put("header-1", "first value");
sentHeaders.put("header-2", "second value");
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// Adds several headers at once
.asciiHeaders(sentHeaders)
// Adds another header, with a static value
.asciiHeader("header").value("value")
// with a Gatling EL string header value
.asciiHeader("header").valueEl("#{headerValue}")
// with a function value
.asciiHeader("header").value(session -> session.getString("headerValue"));
// Extracting a map of headers allows you to reuse these in several requests
val sentHeaders = hashMapOf(
"header-1" to "first value",
"header-2" to "second value"
)
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// Adds several headers at once
.asciiHeaders(sentHeaders)
// Adds another header, with a static value
.asciiHeader("header").value("value")
// with a Gatling EL string header value
.asciiHeader("header").valueEl("#{headerValue}")
// with a function value
.asciiHeader("header").value { session -> session.getString("headerValue") }
// Extracting a map of headers allows you to reuse these in several requests
val sentHeaders = Map(
"header-1" -> "first value",
"header-2" -> "second value"
)
grpc("name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(message)
// Adds several headers at once
.asciiHeaders(sentHeaders)
// Adds another header, with a static value
.asciiHeader("header")("value")
// with a Gatling EL string header value
.asciiHeader("header")("#{headerValue}")
// with a function value
.asciiHeader("header")(session => session("headerValue").as[String])
Or binary format headers (they will use the standard binary marshaller,
io.grpc.Metadata#BINARY_BYTE_MARSHALLER
):
// Extracting a map of headers allows you to reuse these in several requests
Charset utf8 = StandardCharsets.UTF_8;
Map<String, byte[]> sentHeaders = new HashMap<>();
sentHeaders.put("header-1-bin", "first value".getBytes(utf8));
sentHeaders.put("header-2-bin", "second value".getBytes(utf8));
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// Adds several headers at once
.binaryHeaders(sentHeaders)
// Adds another header, with a static value
.binaryHeader("header-bin").value("value".getBytes(utf8))
// with a Gatling EL string header value
.binaryHeader("header-bin").valueEl("#{headerValue}")
// with a function value
.binaryHeader("header-bin").value(session -> session.get("headerValue"));
// Extracting a map of headers allows you to reuse these in several requests
val utf8 = StandardCharsets.UTF_8
val sentHeaders = hashMapOf(
"header-1-bin" to "first value".toByteArray(utf8),
"header-2-bin" to "second value".toByteArray(utf8)
)
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// Adds several headers at once
.binaryHeaders(sentHeaders)
// Adds another header, with a static value
.binaryHeader("header-bin").value("value".toByteArray(utf8))
// with a Gatling EL string header value
.binaryHeader("header-bin").valueEl("#{headerValue}")
// with a function value
.binaryHeader("header-bin").value { session -> session.get("headerValue") }
// Extracting a map of headers allows you to reuse these in several requests
val utf8 = StandardCharsets.UTF_8
val sentHeaders = Map(
"header-1" -> "first value".getBytes(utf8),
"header-2" -> "second value".getBytes(utf8)
)
grpc("name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(message)
// Adds several headers at once
.binaryHeaders(sentHeaders)
// Adds another header, with a static value
.binaryHeader("header")("value".getBytes(utf8))
// with a Gatling EL string header value
.binaryHeader("header-bin")("#{headerValue}")
// with a function value
.binaryHeader("header-bin")(session => session("headerValue").as[Array[Byte]])
If you need to use custom marshallers, you can add headers one at a time with your own io.grpc.Metadata.Key
:
// Define custom marshallers (implementations not shown here)
Metadata.AsciiMarshaller<Integer> intToAsciiMarshaller = null;
Metadata.BinaryMarshaller<Double> doubleToBinaryMarshaller = null;
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// Add headers one at a time (the type of the value must match the type
// expected by the Key's serializer, e.g. Integer for the first one here)
.header(Metadata.Key.of("header", intToAsciiMarshaller)).value(123)
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller)).value(4.56)
// with a Gatling EL string header value
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller)).valueEl("#{headerValue}")
// with a function value
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller)).value(session -> session.get("headerValue"));
// Define custom marshallers (implementations not shown here)
val intToAsciiMarshaller: Metadata.AsciiMarshaller<Int> = TODO()
val doubleToBinaryMarshaller: Metadata.BinaryMarshaller<Double> = TODO()
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// Add headers one at a time (the type of the value must match the type
// expected by the Key's serializer, e.g. Integer for the first one here)
.header(Metadata.Key.of("header", intToAsciiMarshaller)).value(123)
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller)).value(4.56)
// with a Gatling EL string header value
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller)).valueEl("#{headerValue}")
// with a function value
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller)).value { session -> session.get("headerValue") }
// Define custom marshallers (implementations not shown here)
val intToAsciiMarshaller: Metadata.AsciiMarshaller[Int] = ???
val doubleToBinaryMarshaller: Metadata.BinaryMarshaller[Double] = ???
grpc("name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(message)
// Add headers one at a time (the type of the value must match the type
// expected by the Key's serializer, e.g. Int for the first one here)
.header(Metadata.Key.of("header", intToAsciiMarshaller))(123)
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller))(4.56)
// with a Gatling EL string header value
.header[Double](Metadata.Key.of("header-bin", doubleToBinaryMarshaller))("#{headerValue}")
// with a function value
.header(Metadata.Key.of("header-bin", doubleToBinaryMarshaller))(session => session("headerValue").as[Double])
Note that in gRPC, headers are per-stream, not per-message. Even in client or bidirectional streaming methods, request headers are sent only once, when starting the stream:
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("name")
.clientStream(ExampleServiceGrpc.getExampleMethod())
.asciiHeader("header").value("value");
exec(
stream.start(), // Header is sent only once, on stream start
stream.send(message1),
stream.send(message2)
);
val stream = grpc("name")
.clientStream(ExampleServiceGrpc.getExampleMethod())
.asciiHeader("header").value("value")
exec(
stream.start(), // Header is sent only once, on stream start
stream.send(message1),
stream.send(message2)
)
val stream = grpc("request name")
.clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
.asciiHeader("header")("value")
exec(
stream.start, // Header is sent only once, on stream start
stream.send(message1),
stream.send(message2)
)
Also note that keys in gRPC headers are allowed to be associated with more than one value, so adding the same key a second time will simply add a second value, not replace the first one.
Add call credentials
unary serverStream clientStream bidiStream
You can specify call credentials by providing an instance of io.grpc.CallCredentials
. This will override any value set
on the protocol.
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// with a constant
.callCredentials(callCredentials)
// or with an EL string to retrieve CallCredentials already stored in the session
.callCredentials("#{callCredentials}")
// or with a function
.callCredentials(session -> {
var name = session.getString("myUserName");
return callCredentialsForUser(name);
});
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// with a constant
.callCredentials(callCredentials)
// or with an EL string to retrieve CallCredentials already stored in the session
.callCredentials("#{callCredentials}")
// or with a function
.callCredentials { session ->
val name = session.getString("myUserName")!!
callCredentialsForUser(name)
}
grpc("name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(message)
// with a constant
.callCredentials(callCredentials)
// or with an EL string to retrieve CallCredentials already stored in the session
.callCredentials("#{callCredentials}")
// or with a function
.callCredentials { session =>
val name = session("myUserName").as[String]
callCredentialsForUser(name)
}
Add deadline
unary serverStream clientStream bidiStream
You can specify a deadline to use for the request:
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// with a number of seconds
.deadlineAfter(10)
// or with a java.time.Duration
.deadlineAfter(Duration.ofSeconds(10));
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
// with a number of seconds
.deadlineAfter(10)
// or with a java.time.Duration
.deadlineAfter(Duration.ofSeconds(10))
grpc("name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(message)
// with a number of seconds
.deadlineAfter(10)
// or with a scala.concurrent.duration.FiniteDuration
.deadlineAfter(10.seconds)
The actual deadline will be computed just before the start of each gRPC request based on the provided duration.
Add checks
unary serverStream clientStream bidiStream
You can specify one or more checks, to be applied to the response headers, trailers, status, or message:
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
.check(
statusCode().is(Status.Code.OK),
response(ResponseMessage::getMessage).is("hello")
);
grpc("name")
.unary(ExampleServiceGrpc.getExampleMethod())
.send(message)
.check(
statusCode().shouldBe(Status.Code.OK),
response(ResponseMessage::getMessage).shouldBe("hello")
)
grpc("name")
.unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(message)
.check(
statusCode.is(Status.Code.OK),
response((result: ResponseMessage) => result.message).is("hello")
)
See the checks section for more details on gRPC checks.
If you define response checks for server or bidirectional streaming methods, they will be applied to every message received from the server. Other checks are only applied once, at the end of the stream.
Response time policy
serverStream clientStream bidiStream
For streaming methods only, you can specify how to calculate the response time logged for each response message received.
FromStreamStartPolicy
: measure the time since the start of the entire stream. When receiving several response messages on the same stream, they show increasing response times. This is the default because it can always be computed as expected, but it may not be what you are interested in for long-lived server or bidirectional streams.FromLastMessageSentPolicy
: measure the time since the last request message was sent. If no request message was sent previously, falls back toFromStreamStartPolicy
.FromLastMessageReceivedPolicy
: measure the time since the previous response message was received. If this is the first response message received, falls back toFromStreamStartPolicy
.
grpc("name").bidiStream(ExampleServiceGrpc.getExampleMethod())
// Default: from the start of the entire stream
.messageResponseTimePolicy(MessageResponseTimePolicy.FromStreamStart)
// From the time when the last request message was sent
.messageResponseTimePolicy(MessageResponseTimePolicy.FromLastMessageSent)
// From the time the previous response message was received
.messageResponseTimePolicy(MessageResponseTimePolicy.FromLastMessageReceived);
grpc("name").bidiStream(ExampleServiceGrpc.getExampleMethod())
// Default: from the start of the entire stream
.messageResponseTimePolicy(MessageResponseTimePolicy.FromStreamStart)
// From the time when the last request message was sent
.messageResponseTimePolicy(MessageResponseTimePolicy.FromLastMessageSent)
// From the time the previous response message was received
.messageResponseTimePolicy(MessageResponseTimePolicy.FromLastMessageReceived)
grpc("name").bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
// Default: from the start of the entire stream
.messageResponseTimePolicy(FromStreamStartPolicy)
// From the time when the last request message was sent
.messageResponseTimePolicy(FromLastMessageSentPolicy)
// From the time the previous response message was received
.messageResponseTimePolicy(FromLastMessageReceivedPolicy)
Open stream
clientStream bidiStream
For client or bidirectional streaming methods only, you must start the stream to signal that the client is ready to send messages. Only then can you send messages and/or half-close the stream.
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("name").clientStream(ExampleServiceGrpc.getExampleMethod());
exec(stream.start());
val stream = grpc("name").clientStream(ExampleServiceGrpc.getExampleMethod())
exec(stream.start())
val stream = grpc("name").clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
exec(stream.start)
Send a message
unary serverStream clientStream bidiStream
The message sent must be of the type specified in the method descriptor for outbound messages. You can pass a static message, or a function to construct the message from the Gatling Session.
// with a static payload
grpc("name").unary(ExampleServiceGrpc.getExampleMethod())
.send(new RequestMessage("hello"));
// with a function payload
grpc("name").unary(ExampleServiceGrpc.getExampleMethod())
.send(session -> new RequestMessage(session.getString("message")));
// with a static payload
grpc("name").unary(ExampleServiceGrpc.getExampleMethod())
.send(RequestMessage("hello"))
// with a function payload
grpc("name").unary(ExampleServiceGrpc.getExampleMethod())
.send { session -> RequestMessage(session.getString("message")) }
// with a static payload
grpc("name").unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(RequestMessage("hello"))
// with a function payload
grpc("name").unary(ExampleServiceGrpc.METHOD_EXAMPLE)
.send(session => RequestMessage(session("message").as[String]))
For client streaming and bidirectional streaming methods, you can send several messages.
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("name").clientStream(ExampleServiceGrpc.getExampleMethod());
exec(
stream.send(new RequestMessage("first message")),
stream.send(new RequestMessage("second message")),
stream.send(session -> new RequestMessage(session.getString("third-message")))
);
val stream = grpc("name").clientStream(ExampleServiceGrpc.getExampleMethod())
exec(
stream.send(RequestMessage("first message")),
stream.send(RequestMessage("second message")),
stream.send { session -> RequestMessage(session.getString("third-message")) }
)
val stream = grpc("name").clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
exec(
stream.send(RequestMessage("first message")),
stream.send(RequestMessage("second message")),
stream.send(session => RequestMessage(session("third-message").as[String]))
)
Half-close stream
clientStream bidiStream
For client or bidirectional streaming methods only, you can half-close the stream to signal that the client has finished
sending messages. You can then no longer use the send
method on the same stream.
GrpcClientStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("name").clientStream(ExampleServiceGrpc.getExampleMethod());
exec(stream.halfClose());
val stream = grpc("name").clientStream(ExampleServiceGrpc.getExampleMethod())
exec(stream.halfClose())
val stream = grpc("name").clientStream(ExampleServiceGrpc.METHOD_EXAMPLE)
exec(stream.halfClose)
Wait for stream end
serverStream clientStream bidiStream
For streaming methods only, you can use the awaitStreamEnd
method to wait until the server closes the connection.
During that time, you may also still receive response messages from the server.
GrpcBidirectionalStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("name").bidiStream(ExampleServiceGrpc.getExampleMethod());
exec(stream.awaitStreamEnd());
val stream = grpc("name").bidiStream(ExampleServiceGrpc.getExampleMethod())
exec(stream.awaitStreamEnd())
val stream = grpc("name").bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
exec(stream.awaitStreamEnd)
Cancel stream
serverStream clientStream bidiStream
For streaming methods only, you can use the cancel
method to cancel the gRPC stream and prevent any further processing.
GrpcBidirectionalStreamingServiceBuilder<RequestMessage, ResponseMessage> stream =
grpc("name").bidiStream(ExampleServiceGrpc.getExampleMethod());
exec(stream.cancel());
val stream = grpc("name").bidiStream(ExampleServiceGrpc.getExampleMethod())
exec(stream.cancel())
val stream = grpc("name").bidiStream(ExampleServiceGrpc.METHOD_EXAMPLE)
exec(stream.cancel)