Examples
- A simple insecure unary call
- Server reflection
- Metadata using template variables
- Binary data
- Binary fields
- Variable data for unary calls
- Custom parameters
- Protoset
- Config file
- Debug logging
- Client streaming
- Server streaming
- Well Known Types
A simple insecure unary call:
ghz --insecure \
--proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
-d '{"name":"Joe"}' \
0.0.0.0:50051
Server reflection
Or same test using server reflection (just omit -proto option):
ghz --insecure \
--call helloworld.Greeter.SayHello \
-d '{"name":"Joe"}' \
0.0.0.0:50051
Metadata using template variables
A simple unary call with metadata using template actions:
ghz --insecure \
--proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
-d '{"name":"Joe"}' \
-m '{"trace_id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnix}}"}' \
0.0.0.0:50051
Binary data
Using binary data file (see writing a message):
ghz --proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
-B ./hello_request_data.bin \
0.0.0.0:50051
Or using binary from stdin:
ghz --proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
0.0.0.0:50051 < ./hello_request_data.bin
Binary data
Lets say we have the following example proto
syntax = "proto3";
package bytes;
service ImageService {
rpc Save (ImageSaveRequest) returns (ImageSaveResponse) {}
}
message ImageSaveRequest {
string name = 1;
bytes data = 2;
}
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 example. 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:
#!/bin/bash
data=`base64 favicon.ico`
ghz --insecure \
--proto /protos/bytes.proto \
--call bytes.ImageService.Save \
-d "{\"name\":\"icon.ico\", \"data\":\"${data}\"}" \
-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:
ghz --insecure \
--proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
-d '[{"name":"Joe"},{"name":"Bob"}]' \
0.0.0.0:50051
Custom parameters
Custom number of requests and concurrency:
ghz --proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
-d '{"name":"Joe"}' \
-n 2000 \
-c 20 \
0.0.0.0:50051
Using custom number of connections:
ghz --proto ./greeter.proto \
--call helloworld.Greeter.SayHello \
-d '{"name":"Joe"}' \
-n 2000 \
-c 20 \
--connections=10 \
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:
ghz --proto ./greeter.proto \
--call helloworld.Greeter.SayHelloCS \
-d '[{"name":"Joe"},{"name":"Kate"},{"name":"Sara"}]' \
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
protoc --proto_path=. --descriptor_set_out=bundle.protoset *.proto
And then use it as input to ghz with -protoset option:
ghz --protoset ./bundle.protoset \
--call helloworld.Greeter.SayHello \
-d '{"name":"Bob"}' \
-n 1000 -c 10 \
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:
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.
ghz --config ./config.json
Config file settings can be combined with command line arguments. CLI options overwrite config file options.
ghz --config ./config.json -c 20 -n 1000
Debug logging
With debug logging enabled:
ghz --insecure \
--proto ./protos/greeter.proto \
--call helloworld.Greeter.SayHello \
-d '{"name":"Joe"}' -c 5 -n 50 -m '{"request-id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnix}}"}' \
--debug ./logs/debug.json \
0.0.0.0:50051
Client streaming
Client streaming with metadata:
ghz --insecure \
--proto ./protos/route_guide.proto \
--call routeguide.RouteGuide.RecordRoute \
-d '[{ "latitude": 407838351, "longitude": -746143763 }, { "latitude": 419999544, "longitude": -740371136 }, { "latitude": 419611318, "longitude": -746524769 }, { "latitude": 412144655, "longitude": -743949739 }]' \
-m '{"trace_id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnixNano}}"}' \
0.0.0.0:50051
Server streaming
Server streaming with metadata:
ghz --insecure \
--proto ./protos/route_guide.proto \
--call routeguide.RouteGuide.ListFeatures \
-d '{"lo":{"latitude":400000000,"longitude":-750000000},"hi":{"latitude":420000000,"longitude":-730000000}}' \
-m '{"trace_id":"{{.RequestNumber}}", "timestamp":"{{.TimestampUnixNano}}"}' \
0.0.0.0:50051
Well Known Types
Well known types can be used:
Example proto:
syntax = "proto3";
package wrapped;
option go_package = "internal/wrapped";
import "google/protobuf/wrappers.proto";
service WrappedService {
rpc GetMessage (google.protobuf.StringValue) returns (google.protobuf.StringValue);
}
We can test the call:
ghz --insecure \
--proto ./testdata/wrapped.proto \
--call wrapped.WrappedService.GetMessage \
-d '"asdf"' \
0.0.0.0:50051