Tuesday, 22 February 2022

Load testing for gRPC services using ghz

ghz is a command line utility and Go package for load testing and benchmarking gRPC services. It is intended to be used for testing and debugging services locally, and in automated continuous integration environments for performance regression testing. 

The client side can be parametrized with number of connections to use, number of requests to send concurrently, CPU usage limit, and payload size. Request rate can also be optionally limited. It is also possible to define the payload to be sent.

Installation:

Download a prebuilt executable binary for your operating system from the GitHub releases page.
Unzip the archive and place the executable binary wherever you would like to run it from. Later, add the location of the directory in the PATH variable (if you would like the ghz command to be available everywhere).

















Once the PATH is set, Open the terminal and check with install ghz version using the command ghz --version

The ghz command line has numerous command line options. You can run the command ghz --help to view all available options.


































Load Options:

: Save/place the Proto file in the current working directory.
: Create a .json file that includes all the request body parameters in an array.
 : Finally, issue the following command to load test any gRPC service using the command 

ghz --insecure --proto <path-to-proto file> --call <RPC-method-you-want-to-test> -n 100000 -D <data-file.json> <host URL/ip:port>

This is a walkthrough of different load options available to control the rate in requests per second (RPS) that ghz attempts to make to the server. All examples are done using a simple unary gRPC call

ghz --async --proto /helloworld.proto --call helloworld.Greeter/SayHello -c 10 -n 100 --rps 200 127.0.0.1:50051

You can view the different call/load option on the ghz docs

Configuration Files 

All the call options can be specified in JSON or TOML config files and it is used as an input via the -config option. Below is a sample config file saved as SayHello.json

  
  {
    "proto": "/path/to/greeter.proto",
    "call": "helloworld.Greeter.SayHello",
    "total": 2000,
    "concurrency": 50,
    "data": {
        "name": "Joe"
    },
    "metadata": {
        "foo": "bar",
        "trace_id": "{{.RequestNumber}}",
        "timestamp": "{{.TimestampUnix}}"
    },
    "import-paths": [
        "/path/to/protos"
    ],
    "max-duration": "10s",
    "host": "0.0.0.0:50051"
} 
Once the JSON file is ready then open the terminal, run the command ghz --config=SayHello.json   in the project directory. 

The Output will be displayed on the Terminal 
 Summary:
  Count:    10000
  Total:    50.05 s
  Slowest:  56.17 ms
  Fastest:  50.17 ms
  Average:  50.58 ms
  Requests/sec: 199.79

Response time histogram:
  50.167 [1]    |
  50.768 [8056] |∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎∎
  51.368 [1900] |∎∎∎∎∎∎∎∎∎
  51.968 [37]   |
  52.568 [1]    |
  53.169 [0]    |
  53.769 [0]    |
  54.369 [1]    |
  54.969 [1]    |
  55.569 [0]    |
  56.170 [3]    |

Latency distribution:
  10 % in 50.32 ms
  25 % in 50.40 ms
  50 % in 50.56 ms
  75 % in 50.72 ms
  90 % in 50.88 ms
  95 % in 51.00 ms
  99 % in 51.23 ms

Status code distribution:
  [OK]   10000 responses
If the host is incorrect then the error description will be mentioned in the Error distribution
 
Summary:
  Count:        10000
  Total:        50.05 s
  Slowest:      0 ns
  Fastest:      0 ns
  Average:      305.33 ms
  Requests/sec: 199.80

Response time histogram:

Latency distribution:

Status code distribution:
  [Unavailable]   10000 responses

Error distribution:
  [10000]   rpc error: code = Unavailable desc = connection 
            error: desc = "transport: Error while dialing dial tcp 127.0.0.1:50051: connectex: No connection could be made because the target machine actively refused it."
Incase, your request requires additional information, for example, the request header should include authorization or login credentials or bearer token then you can create a JSON file and add those information into the JSON file. Later, pass that file in the ghz command using the flag option -M ./metadata.json

If you would like to save the response in to a JSON file then you can use the flag option -O json in your ghz command and it will create a json file in the Project Directory.

Some cool ghz features include:

- Ability to use serialized binary messages
- JSON/TOML config files as opposed to command line arguments
- Support for streaming RPCs

ghz-web

ghz-web is a completely separate complimentary application for storing, viewing and comparing the results of test runs over time. It would have separate config not related to your ghz runs.

ghz-web setup:

Create a config file web.yml for ghz-web and place it in the same directory where the ghz-web exe file is present




























Run it using ghz-web -config web.yml














Go to http://localhost:3000/ and you should see a UI. Create a project. Take note of the ID.

The basic general idea is that ghz would be used to generate JSON report and the JSON report would be ingested using curl or similar tool (httpie.io) to save the results so that they can be stored, viewed, compared, and tracked over time. You can download httpie.io from https://httpie.io/download and installed it on to our windows system. Once httpie.io is installed successfully and a ghz json output file is present then run the web server ingest API endpoint to automatically creates a new project, ingests a report and assigns it to the project.

POST /api/projects/:id/ingest
ghz -insecure --config=SayHello.json 127.0.0.1:50051 -M ./metadata.json -O json | http post localhost:3000/api/projects/1/ingest
The demo web Projects are displayed on the browser. 


 








Clicking on the project will display the different load/performance metrics associated with the call

 






































Feel free to post your questions/issue and I will be happy to explore and address them. 

1 comment:

  1. if you have failed to run ghz-web before ingesting the json data then the below error will be displayed
    -----------------------------------------------------------
    http: error: ConnectionError: HTTPConnectionPool(host='localhost', port=3000): Max retries exceeded with url: /api/projects/1/ingest (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it')) while doing a POST request to URL: http://localhost:3000/api/projects/1/ingest

    ReplyDelete