Chapter 13 · Non-HTTP Protocol Testing
Chapter goals: Break through HTTP limits by testing TCP Socket, gRPC, XML-RPC and other protocols with custom User classes, and understand the gevent monkey-patching constraints.
13.1 Extension Principle: The request Event
Locust natively supports only HTTP/HTTPS, but the architecture leaves an extension point. The core idea:
- Inherit from
User(notHttpUser) to create a custom client; - After each protocol call, manually fire the
requestevent so Locust records statistics.
from locust import User, task, between, events
from locust.exception import LocustError
import socket
import time
class TcpClient:
def __init__(self, host, port):
self.host = host
self.port = port
self.socket = None
def connect(self):
self.socket = socket.create_connection((self.host, self.port))
def send_and_receive(self, payload: bytes) -> bytes:
start_time = time.time()
start_perf = time.perf_counter()
try:
self.socket.sendall(payload)
response = self.socket.recv(4096)
elapsed = time.perf_counter() - start_perf
# Fire the request event so Locust records stats
events.request.fire(
request_type="TCP",
name="send_recv",
response_time=elapsed * 1000,
response_length=len(response),
exception=None,
context={},
)
return response
except Exception as e:
elapsed = time.perf_counter() - start_perf
events.request.fire(
request_type="TCP", name="send_recv",
response_time=elapsed * 1000,
response_length=0, exception=e, context={},
)
raise
def close(self):
if self.socket:
self.socket.close()13.2 gevent Monkey-Patching Constraints
Key limitation
Locust's core uses gevent cooperative concurrency. All libraries under test must be patchable by gevent — pure Python libraries (using the standard socket module) work out of the box; C-extension libraries (like psycopg2) cannot be patched and will block the entire process.
| Library | Patchable? | Workaround |
|---|---|---|
socket / ssl | ✅ Works out of the box | — |
requests / urllib3 | ✅ Built-in | — |
pymysql / asyncpg | ✅ Pure Python | — |
psycopg2 (PostgreSQL) | ❌ C extension | Use psycogreen patch |
redis-py (hiredis) | ⚠️ Partial | Disable hiredis parser |
13.3 XML-RPC Load Testing Example
The official docs provide an XML-RPC client wrapper example. Start a test server first:
# server.py
from xmlrpc.server import SimpleXMLRPCServer
import time, random
def add(x, y):
time.sleep(random.uniform(0.01, 0.05))
return x + y
def query_database(query):
time.sleep(random.uniform(0.05, 0.2))
return [{"id": 1, "result": 42}]
server = SimpleXMLRPCServer(("localhost", 8000))
server.register_function(add, "add")
server.register_function(query_database, "db.query")
server.serve_forever()Then the Locust test:
import xmlrpc.client
from locust import User, task, events
class XmlRpcUser(User):
wait_time = between(0.5, 2)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.client = xmlrpc.client.ServerProxy("http://localhost:8000")
@task
def add_numbers(self):
start = time.perf_counter()
try:
result = self.client.add(10, 20)
elapsed = (time.perf_counter() - start) * 1000
events.request.fire(
request_type="XML-RPC", name="add",
response_time=elapsed, response_length=len(str(result)),
exception=None,
)
except Exception as e:
elapsed = (time.perf_counter() - start) * 1000
events.request.fire(
request_type="XML-RPC", name="add",
response_time=elapsed, response_length=0,
exception=e,
)13.4 Chapter Summary
- Inherit from
User(notHttpUser) for non-HTTP protocols; - Manually fire
events.request.fire()after each call to record stats; - gevent monkey-patching is required — C-extension libraries may block;
- Custom TCP, XML-RPC, and gRPC clients all follow the same pattern.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. To test a non-HTTP protocol in Locust, which class should you inherit?
2. Why can C-extension libraries like psycopg2 block Locust?
3. What is the minimum code needed to record a custom-protocol request in Locust stats?
4. Which library below CANNOT be used directly with Locust without a workaround?
🛠️ Hands-On Exercises
- Write a custom TCP User that connects to a local echo server, sends messages, and fires
events.request.fire()for each round-trip. - Replace the TCP client with an XML-RPC client; verify the request appears in the Web UI under the "XML-RPC" request type.
- Try using psycopg2 directly in a Locust task; observe the blocking behavior and fix it with psycogreen.
Proceed to Chapter 14: asyncio Support.