How to Load‑Test Multiple Service Endpoints in Proportion with a Custom Java Framework
This article explains how to conduct proportional load testing for a microservice that exposes several APIs by using a custom Java performance‑testing framework, covering request deep‑copy handling, MarkRequest cloning, and a complete code example that drives concurrent requests according to a 1:2:3 ratio.
Problem
In micro‑service architectures a single service often exposes multiple HTTP endpoints. Testing each endpoint in isolation does not reflect the service’s overall throughput, especially when production traffic follows a fixed distribution among the endpoints. A method is required to generate load that respects a predefined traffic ratio while keeping request objects isolated per thread.
Solution Overview
The approach uses a custom performance‑testing framework (second edition) to:
Obtain a separate HttpRequestBase instance for each target endpoint.
Deep‑copy the request objects so that concurrent threads do not share mutable state.
Inject a unique requestid header via a MarkRequest implementation.
Generate request‑execution threads according to a ratio (e.g., 1:2:3) supplied on the command line.
Run all threads concurrently with a Concurrent runner.
Deep Copy of HttpRequestBase
Because HttpRequestBase is mutable, each thread must work on its own copy. The framework provides a utility that clones the request via Java serialization; therefore the request class (or any subclass) must implement Serializable. If serialization is not feasible, overriding clone() in the request class (or in MarkRequest) is an alternative, which is especially convenient when the test script is written in Groovy.
MarkRequest Implementation
A custom MarkRequest adds a unique identifier to every outgoing request. The identifier is built from a random 4‑character string and a timestamp, then stored in the requestid header.
MarkRequest mark = new MarkRequest() {
private static final long serialVersionUID = -2751325651625435073L;
String m;
@Override
public String mark(HttpRequestBase request) {
request.removeHeaders("requestid");
m = (m == null) ? RString.getStringWithoutNum(4) : m;
String value = "fun_" + m + CONNECTOR + Time.getTimeStamp();
request.addHeader("requestid", value);
return value;
}
};Command‑Line Parameters
thread – number of concurrent thread groups (default 2).
times – number of repetitions each request thread performs (default 5).
split – traffic ratio expressed as a colon‑separated string, e.g., 1:2:3. The value is split into an array and used to determine how many threads are created for each endpoint.
Generating Request Threads
Three distinct request objects are retrieved from the test library (one per endpoint). For each element of the split array, the script creates the corresponding number of RequestThreadTime instances, each wrapping the appropriate request and the repetition count.
def requests = []
split[0].times { requests << new RequestThreadTime(request1, times) }
split[1].times { requests << new RequestThreadTime(request2, times) }
split[2].times { requests << new RequestThreadTime(request3, times) }These request‑thread objects are then duplicated across the desired number of thread groups:
List<HttpRequestBase> res = []
thread.times { res << requests }Concurrent Execution
The assembled list of request groups is handed to the Concurrent runner, which starts all threads simultaneously. A descriptive message records the applied ratio, thread count, and iteration count.
new Concurrent(res, "按照比例${split}压测线程数${thread}次数${times}").start()
allOver();Result
By following this procedure testers can simulate realistic traffic distribution across multiple service endpoints, guarantee that each request instance is isolated per thread, and integrate the load generation seamlessly into the existing performance‑testing framework.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
