An elegant Java client for Locust
An elegant Java client for Locust
IMHO, Locust4j a basic implementation for Locust clients written in Java. It has so many restrictions, including:
The code base is not organized well
Not so performant with blocking queues
AbstractTask
does NOT support async operations
Slow start-up time (mostly by Utils.getNodeID()
)
Insufficient usage of threading. Locust4j
will use N
threads to stimulate N
users. A big N
will result in
performance degradation (due to context switching of threads)
Not well-supported for async operations
Based on implementation of Locust4j
, Swarm
is completely re-written to address all issues above, with some
additional features:
Auto detect connection status to Locust master hosts and reponse back.
A better model to stimulate concurrent users (with disruptors)
LMAX Disruptor
for non-blocking queues
Google Guava
for rate limiting
Jeromq
+ MessagePack
for working with Locust masters
Before using Swarm
, you must declare it as a dependency of your Maven project. For example:
<dependency>
<groupId>com.bigsonata.swarm</groupId>
<artifactId>locust-swarm</artifactId>
<version>1.1.6</version>
</dependency>
You also have to add following dependencies:
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.msgpack</groupId>
<artifactId>msgpack-core</artifactId>
<version>0.8.13</version>
</dependency>
<dependency>
<groupId>org.zeromq</groupId>
<artifactId>jeromq</artifactId>
<version>0.4.3</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
Swarm
uses the term of Cron
to refer to repeating tasks defined by you.
Life cycle of a Cron-based instance can be described in the following diagram:
Swarm
is responsible for cloning, initializing crons.
// Here is an abridged version of class Cron
public abstract class Cron implements Cloneable, Runnable {
public Cron(Props props) {
this.props = props;
}
public abstract void process();
public abstract Cron clone();
public void recordFailure(long responseTime, String error) {
// ..
}
public void recordSuccess(long responseTime, long contentLength) {
// ..
}
public void recordSuccess(long responseTime) {
// ..
}
@Override
public void run() {
// ...
process();
}
public abstract void dispose();
public abstract void initialize();
}
Each cron has its own props
which defines type & name (to display on the Web interface):
public class Props {
protected String type = "default";
protected String name = "cron";
protected int weight = 1;
protected boolean async = false;
public static Props create() {
return new Props();
}
public static Props createAsync() {
return new Props()
.setAsync(true);
}
public Props setType(String type) {
// ..
}
public Props setName(String name) {
// ..
}
public Props setWeight(int weight) {
// ..
}
public Props setAsync(boolean async) {
// ..
}
}
NOTE:
A cron must specify how it works: synchronously or asynchronously
Inside each cron, there are 4 must-overridden methods
clone
: you should let Swarm
know how to clone your Cron :Dinitialize
: It's a great place for you to initiate connections (if you want to use pooling mechanism)process
: which is repeatedly executed to perform your tests and report resultsdispose
: Swarm will invoke this method to dispose any resources allocated by cronsYou need to define your loading test by deriving class Cron
:
For example, in our AsyncCron
:
public class TimerCron extends Cron {
public TimerCron() {
super(Props.create().setName("timer").setAsync(false));
}
@Override
public void process() {
try {
System.out.println("> Processing...");
long start = System.currentTimeMillis();
Thread.sleep(1000);
long duration = System.currentTimeMillis() - start;
recordSuccess(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public Cron clone() {
return new TimerCron();
}
@Override
public void dispose() {
}
@Override
public void initialize() {
}
}
After defining your crons, finally you need to instruct Swarm
to start:
Locust.Builder.newInstance()
.setMasterHost(masterHost)
.setMasterPort(masterPort)
// Optionally set the interval (in ms) to report statistics
// Default: 2000
.setStatInterval(2000)
// Optionally set a seed number to generate nodeId
.setRandomSeed(0)
// Optionally set the number of threads to stimulate Crons
// Default: 8
.setThreads(8)
// Optionally set the number of maximum requests per second
.setMaxRps(1000)
// Register cron tasks
.setCrons(new TimerCron());
.build()
To effectively benchmark with Locust, we may need to use connection pooling
See the demo in src/main/java/com.bigsonata/example
If you find anything wrong or would like to contribute in any way, feel free to create a pull request/open an issue/send me a message. Any comments are welcome!
v1.1.6
v1.1.5
Occurrences
(with
this PR)v1.1.4
v1.1.3
v1.1.0
setCrons
v1.0.1
v1.0.0
v0.1.2
v0.1.0