h2load is a command-line tool that can be used to load test HTTP/2 servers. Locust is a Python-based load testing tool that can be used to simulate user behavior. This article will show you how to use h2load and Locust to load test HTTP/2 servers.
Load Testing HTTP/2 Servers with h2load and Locust
Load testing HTTP/2 servers requires specialized tools that can effectively simulate the multiplexing and header compression features of the protocol. While tools like ApacheBench (ab) and wrk are excellent for HTTP/1.1, they often fall short when it comes to accurately stressing HTTP/2 environments. This is where h2load and Locust shine, each offering a distinct approach to understanding your server’s performance under load.
h2load, part of the nghttp2 suite, is a high-performance command-line client designed specifically for HTTP/2. It excels at generating a massive number of concurrent requests and connections, making it ideal for measuring raw throughput and latency of your HTTP/2 endpoint. Its strength lies in its simplicity and speed for focused, high-volume testing.
Locust, on the other hand, is a Python-based distributed load testing tool. It allows you to define user behavior in Python code, enabling more sophisticated simulations of real-world user interactions. While it can also test HTTP/2, its primary advantage is the ability to model complex user flows, ramp up users gradually, and provide rich, interactive web-based statistics.
Let’s start with h2load for raw performance metrics.
Using h2load for High-Volume Throughput Testing
h2load’s primary function is to hammer your server with as many requests as possible. The most critical aspect of its configuration is defining the target server and the request rate.
Basic Usage:
The simplest way to use h2load is to point it at your HTTP/2 server’s URL. Ensure your server is configured to accept HTTP/2 connections, typically over TLS (h2 or h2c).
h2load -c 100 -d 10 -T 1000000 https://your-http2-server.com/
-c 100: This sets the number of concurrent HTTP/2 connections to 100. This is crucial for HTTP/2 as it leverages multiplexing over fewer connections compared to HTTP/1.1.-d 10: This specifies the number of requests to issue per connection. In this case, 10 requests per connection.-T 1000000: This sets the total number of requests to 1 million.
Interpreting h2load Output:
h2load will output statistics like:
- RPS (Requests per second): The overall rate at which requests were served.
- Latency (Average, p95, p99): The time taken to serve requests.
- Throughput (MB/s): The amount of data transferred per second.
A common pitfall is not setting -d high enough, leading to connections being opened and closed rapidly, which isn’t representative of sustained load. Alternatively, if your server is configured to limit concurrent connections or requests per connection, you might see errors or incomplete test runs.
Advanced h2load Options:
- Custom Request Bodies: You can specify a file for POST requests:
echo "some data" > request.bin h2load -c 100 -d 10 -T 1000000 -H "Content-Type: application/octet-stream" -f request.bin https://your-http2-server.com/post-endpoint - HTTP/2 Settings: You can override default HTTP/2 settings using
-s:
This can be useful for tuning your server’sh2load -c 100 -d 10 -T 1000000 -s "SETTINGS_MAX_CONCURRENT_STREAMS=200" https://your-http2-server.com/SETTINGS_MAX_CONCURRENT_STREAMSparameter, which dictates how many requests can be in flight on a single connection.
Using Locust for Realistic User Simulation
Locust allows you to define complex user scenarios, making it suitable for testing how your application behaves under load that mimics real user interactions, not just raw request volume.
Setting up Locust:
- Install Locust:
pip install locust - Create a
locustfile.py: This Python file defines your simulated users.
Example locustfile.py for HTTP/2:
Locust uses the requests library by default, which supports HTTP/2 if httpx is installed.
from locust import HttpUser, task, between
import httpx # Ensure httpx is installed: pip install httpx
class Http2User(HttpUser):
wait_time = between(1, 5) # Wait 1-5 seconds between tasks
host = "https://your-http2-server.com"
# Specify the client to use
client_class = httpx.Client
@task
def index(self):
# The httpx client automatically uses HTTP/2 if available and configured
self.client.get("/")
@task
def get_item(self):
item_id = 123
self.client.get(f"/items/{item_id}")
# You can add more tasks to simulate different user actions
HttpUser: The base class for defining a user.wait_time: Defines the pause between tasks for each user.host: The base URL of your HTTP/2 server.client_class = httpx.Client: This is key. By default, Locust usesrequests. Setting this tohttpx.Clienttells Locust to usehttpx, which has excellent HTTP/2 support. You’ll need to installhttpx(pip install httpx).
Running Locust:
- Start the Locust web UI:
locust -f locustfile.py --host=https://your-http2-server.com - Open your browser to
http://localhost:8089. - Enter the number of users to simulate and the spawn rate.
- Click "Start swarming".
Interpreting Locust Output:
The Locust web UI provides real-time statistics on:
- Number of users: Currently active simulated users.
- RPS: Requests per second.
- Response Times: Average, median, 95th percentile, etc.
- Failures: Number of requests that resulted in errors.
Locust’s strength is in its ability to model user journeys. For example, you might have a user who first requests the homepage, then logs in, then views a product, and finally adds it to a cart. You can model this sequence using Locust tasks.
Leveraging HTTP/2 Features in Locust:
While httpx handles the HTTP/2 protocol details, you can still influence performance by:
- Increasing concurrent users: This simulates more users interacting simultaneously, leading to more concurrent streams across fewer connections.
- Defining complex task sequences: This can expose bottlenecks in your application logic or resource management under realistic load.
- Using
httpx.Clientwithhttp2=True: Althoughhttpxusually auto-detects and enables HTTP/2 when available, you can explicitly set it. However, for most cases, simply usinghttpx.Clientis sufficient as it prioritizes HTTP/2 if the server supports it.
The most counterintuitive aspect of HTTP/2 load testing is understanding that a single TCP connection can carry dozens or even hundreds of requests concurrently. Therefore, a high number of users in Locust doesn’t necessarily mean a high number of TCP connections. Instead, it means a high number of streams multiplexed over a smaller number of connections, which is exactly what HTTP/2 is designed for. Measuring how many streams your server can handle efficiently on a per-connection basis is often more revealing than just raw connection counts.
By combining h2load for raw throughput and h2load’s ability to simulate massive request volumes with Locust’s user behavior modeling, you can gain a comprehensive understanding of your HTTP/2 server’s performance and identify potential bottlenecks under various load conditions.