Examples

A simple insecure unary call:

  1. ghz --insecure \
  2. --proto ./greeter.proto \
  3. --call helloworld.Greeter.SayHello \
  4. -d '{"name":"Joe"}' \
  5. 0.0.0.0:50051

Server reflection

Or same test using server reflection (just omit -proto option):

  1. ghz --insecure \
  2. --call helloworld.Greeter.SayHello \
  3. -d '{"name":"Joe"}' \
  4. 0.0.0.0:50051

Metadata using template variables

A simple unary call with metadata using template actions:

  1. ghz --insecure \
  2. --proto ./greeter.proto \
  3. --call helloworld.Greeter.SayHello \
  4. -d '{"name":"Joe"}' \
  5. -m '{"trace_id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnix}}"}' \
  6. 0.0.0.0:50051

Binary data

Using binary data file (see writing a message):

  1. ghz --proto ./greeter.proto \
  2. --call helloworld.Greeter.SayHello \
  3. -B ./hello_request_data.bin \
  4. 0.0.0.0:50051

Or using binary from stdin:

  1. ghz --proto ./greeter.proto \
  2. --call helloworld.Greeter.SayHello \
  3. 0.0.0.0:50051 < ./hello_request_data.bin

Binary data

Lets say we have the following example proto

  1. syntax = "proto3";
  2. package bytes;
  3. service ImageService {
  4. rpc Save (ImageSaveRequest) returns (ImageSaveResponse) {}
  5. }
  6. message ImageSaveRequest {
  7. string name = 1;
  8. bytes data = 2;
  9. }
  10. message ImageSaveResponse {}

One way to create the request for a test is to use the binary data option -B to specify a binary file like we did in the prior exammple. Simply serialize the whole message to a binary file first.

Alternatively we can use base64 string representation of the image as the value for the data field.

If we have a file favicon.ico that we want to send in the message, we can have a simple bash script to encode and send as part of the JSON input:

  1. #!/bin/bash
  2. data=`base64 favicon.ico`
  3. ghz --insecure \
  4. --proto /protos/bytes.proto \
  5. --call bytes.ImageService.Save \
  6. -d "{\"name\":\"icon.ico\", \"data\":\"${data}\"}" \
  7. -c 1 -n 1 0.0.0.0:50051

On the server side we would have to decode from base64 into the binary data, which depending on the specifics may not be desireable. We could additionally add a bool is_base64 = 3; flag field to specify if the message is base64 encoded. But the complexity of this workaround may be why, if possible, saving the whole test message may be more appropriate.

Variable data for unary calls

Round-robin of messages for unary call:

  1. ghz --insecure \
  2. --proto ./greeter.proto \
  3. --call helloworld.Greeter.SayHello \
  4. -d '[{"name":"Joe"},{"name":"Bob"}]' \
  5. 0.0.0.0:50051

Custom parameters

Custom number of requests and concurrency:

  1. ghz --proto ./greeter.proto \
  2. --call helloworld.Greeter.SayHello \
  3. -d '{"name":"Joe"}' \
  4. -n 2000 \
  5. -c 20 \
  6. 0.0.0.0:50051

Using custom number of connections:

  1. ghz --proto ./greeter.proto \
  2. --call helloworld.Greeter.SayHello \
  3. -d '{"name":"Joe"}' \
  4. -n 2000 \
  5. -c 20 \
  6. --connections=10 \
  7. 0.0.0.0:50051

10 connections will be shared among 20 goroutine workers. Each pair of 2 goroutines will share a single connection.

Client streaming data can be sent as an array, each element representing a single message:

  1. ghz --proto ./greeter.proto \
  2. --call helloworld.Greeter.SayHelloCS \
  3. -d '[{"name":"Joe"},{"name":"Kate"},{"name":"Sara"}]' \
  4. 0.0.0.0:50051

Protoset

If a single object is given for data it is sent as every message.

We can also use .protoset files which can bundle multiple protocol buffer files into one binary file.

Create a protoset

  1. protoc --proto_path=. --descriptor_set_out=bundle.protoset *.proto

And then use it as input to ghz with -protoset option:

  1. ghz --protoset ./bundle.protoset \
  2. --call helloworld.Greeter.SayHello \
  3. -d '{"name":"Bob"}' \
  4. -n 1000 -c 10 \
  5. 0.0.0.0:50051

Note that only one of -proto or -protoset options will be used. -proto takes precedence.

Alternatively ghz can be used with Prototool using the descriptor-set command:

  1. ghz --protoset $(prototool descriptor-set --include-imports --tmp) ...

Config file

Finally we can specify all settings, including the target host, conveniently in a JSON or TOML config file.

  1. ghz --config ./config.json

Config file settings can be combined with command line arguments. CLI options overwrite config file options.

  1. ghz --config ./config.json -c 20 -n 1000

Debug logging

With debug logging enabled:

  1. ghz --insecure \
  2. --proto ./protos/greeter.proto \
  3. --call helloworld.Greeter.SayHello \
  4. -d '{"name":"Joe"}' -c 5 -n 50 -m '{"request-id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnix}}"}' \
  5. --debug ./logs/debug.json \
  6. 0.0.0.0:50051

Client streaming

Client streaming with metadata:

  1. ghz --insecure \
  2. --proto ./protos/route_guide.proto \
  3. --call routeguide.RouteGuide.RecordRoute \
  4. -d '[{ "latitude": 407838351, "longitude": -746143763 }, { "latitude": 419999544, "longitude": -740371136 }, { "latitude": 419611318, "longitude": -746524769 }, { "latitude": 412144655, "longitude": -743949739 }]' \
  5. -m '{"trace_id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnixNano}}"}' \
  6. 0.0.0.0:50051

Server streaming

Server streaming with metadata:

  1. ghz --insecure \
  2. --proto ./protos/route_guide.proto \
  3. --call routeguide.RouteGuide.ListFeatures \
  4. -d '{"lo":{"latitude":400000000,"longitude":-750000000},"hi":{"latitude":420000000,"longitude":-730000000}}' \
  5. -m '{"trace_id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnixNano}}"}' \
  6. 0.0.0.0:50051

Well Known Types

Well known types can be used:

Example proto:

  1. syntax = "proto3";
  2. package wrapped;
  3. option go_package = "internal/wrapped";
  4. import "google/protobuf/wrappers.proto";
  5. service WrappedService {
  6. rpc GetMessage (google.protobuf.StringValue) returns (google.protobuf.StringValue);
  7. }

We can test the call:

  1. ghz --insecure \
  2. --proto ./testdata/wrapped.proto \
  3. --call wrapped.WrappedService.GetMessage \
  4. -d '"asdf"' \
  5. 0.0.0.0:50051