Skip to content

Load Test gRPC Services with SLOs

Overview

Iter8's load-test-grpc experiment chart can be used to generate call requests for gRPC services, collect built-in latency and error-related metrics, and validate service-level objectives (SLOs).

Use-cases: Rapid testing, validation, safe rollouts, and continuous delivery (CD) of gRPC services are the motivating use-cases for this experiment type. If the gRPC service satisfies the SLOs, it may be safely rolled out, for example, from a test environment to a production environment.


Your first experiment provides a basic example of using the load-test-http experiment chart. This tutorial provides additional examples.

Before you try these examples
  1. Install Iter8.
  2. Choose a language, and follow the linked instructions to run the gRPC sample app.

    Update step is not needed

    The linked instructions show how to update the app, and re-run the updated app. For the purpose of this tutorial, there is no need to update and re-run. Running the basic service is sufficient.

  3. Download experiment chart.

    iter8 hub -e load-test-grpc
    cd load-test-grpc
    


Basic example

Load test the gRPC sample service with host value 127.0.0.1:50051, fully-qualified method name (call) helloworld.Greeter.SayHello, and defined by the Protocol Buffer file located at the protoURL.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto"

View a report of this experiment as described in the quick start tutorial.

Load profile

Control the characteristics of the load generated by the load-test-grpc experiment by setting the number of requests (total)/duration (duration), the number of requests per second (rps), number of connections to use (connections), and the number of concurrent request workers to use in each connection (concurrency).

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set data.name="frodo" \
          --set total=500 \
          --set rps=25 \
          --set concurrency=50 \
          --set connections=10

The duration value may be any Go duration string.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set data.name="frodo" \
          --set duration="20s" \
          --set rps=25 \
          --set concurrency=50 \
          --set connections=10

When you set total and qps, the duration of the load test is automatically determined. Similarly, when you set duration and qps, the number of requests is automatically determined. If you set both total and duration, the former will be ignored.


Call data

gRPC calls may include data serialized as Protocol Buffer messages. Supply them as values, or by pointing to JSON or binary files containing the data.

The protobuf file specifying the gRPC service used in this tutorial defines the following HelloRequest message format:

message HelloRequest {
  string name = 1;
}

Suppose you want include the following HelloRequest message with every call.

name: frodo

To do so, run the Iter8 experiment as follows.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set data.name="frodo"

Nested data

Call data may be nested. For example, consider the data:

name: frodo
realm:
  planet: earth
  location: middle
You can set the above data in iter8 run as follows:
--set data.name="frodo" \
--set data.realm.planet="earth" \
--set data.realm.location="middle" 

Suppose the call data you want to send is contained in a JSON file and hosted at the url https://location.of/data.json. Iter8 can fetch this JSON file and use the data contained in it during the gRPC load test. To do so, run the experiment as follows.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set dataURL="https://location.of/data.json"

Suppose that call data you want to send is contained in a binary file as a serialized binary message or multiple count-prefixed messages, and hosted at the url https://location.of/data.bin. Iter8 can fetch this binary file and use the data contained in it during the gRPC load test. To do so, run the experiment as follows.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set binaryDataURL="https://location.of/data.bin"

data vs dataURL vs binaryDataURL

If the call data is shallow and has only a few fields, setting the data value directly is the easiest of the three approaches. If it is deeply nested, or contains many fields, storing the data as a JSON or binary file, and providing the dataURL or binaryDataURL value might be the easier approach. When more than one of these options are specified, the data field takes precedence over the dataURL field which in turn takes precedence over the binaryDataURL field.


Call metadata

gRPC calls may include metadata which is information about a particular call. Supply them as values, or by pointing to a JSON file containing the metadata.

You can supply metadata of type map[string]string (i.e., a map whose keys and values are strings) in the gRPC load test. Suppose you want to use the following metadata.

darth: vader
lord: sauron
volde: mort

To do so, run the Iter8 experiment as follows.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set metadata.darth="vader" \
          --set metadata.lord="sauron" \
          --set metadata.volde="mort"

Suppose the call metadata you want to send is contained in a JSON file and hosted at the url https://location.of/metadata.json. Iter8 can fetch this JSON file and use its contents as the metadata during the gRPC load test. To do so, run the experiment as follows.

iter8 run --set-string host="127.0.0.1:50051" \
          --set-string call="helloworld.Greeter.SayHello" \
          --set-string protoURL="https://raw.githubusercontent.com/grpc/grpc-go/master/examples/helloworld/helloworld/helloworld.proto" \
          --set metadataURL="https://location.of/metadata.json"

metadata vs metadataURL

If the call metadata is shallow and has only a few fields, setting the metadata value directly is the easier approach. If it is deeply nested, or contains many fields, storing the data as a JSON binary file, and providing the metadataURL value might be the easier approach. When both these options are specified, the metadata field takes precedence over the metadataURL field.

Back to top