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