Locust Load tool: Understanding terms and concepts

Locust is a event-based and Python code based load test tool. It is easy-to-use, distributed and supported hundreds of thousands of users.

Why Locust ?

here is some words from its documentation:

Locust was created because we were fed up with existing solutions. None of them are solving the right problem and to me, they are missing the point. We’ve tried both Apache JMeter and Tsung. Both tools are quite OK to use; we’ve used the former many times benchmarking stuff at work. JMeter comes with a UI, which you might think for a second is a good thing. But you soon realize it’s a PITA to “code” your testing scenarios through some point-and-click interface. Secondly, JMeter is thread-bound. This means for every user you want to simulate, you need a separate thread. Needless to say, benchmarking thousands of users on a single machine just isn’t feasible.

Tsung, on the other hand, does not have these thread issues as it’s written in Erlang. It can make use of the light-weight processes offered by BEAM itself and happily scale up. But when it comes to defining the test scenarios, Tsung is as limited as JMeter. It offers an XML-based DSL to define how a user should behave when testing. I guess you can imagine the horror of “coding” this. Displaying any sorts of graphs or reports when completed requires you to post-process the log files generated from the test. Only then can you get an understanding of how the test went.

https://docs.locust.io/en/stable/what-is-locust.html#background

Now, Let’s understand some Locust terms and its concepts before jumping to it.

Event-based: not like Jmeter which has a thread-based model, which allocates a separate thread for each user, Locust On the contrary, is based on events and async approach, with gevent coroutine as the cornerstone of the whole process. This implementation allows the Locust framework to easily simulate thousands of concurrent users on a single machine.

Locust files: the Python modules that you define your user behaviors for load test. The default locust file name that Locust will looking for is “locustfile.py“. Just put the your code in locustfile.py in your current directory and run command locust . You also can name the locust file with a customized name and place it somewhere else, then you will need to specify it using -f with locust command.

User/HttpUser/FastHttpUser classes:

User class represents one user (or a swarming locust if you will). Locust will spawn (hatch) one instance of the User class for each user that is being simulated. This class should usually be subclassed by a class that defines some kind of client. If load testing an HTTP system, you probably want to use the HttpUser class.

HttpUser represents an HTTP “user” which is to be hatched and attack the system that is to be load tested. This class creates a *client* attribute on instantiation which is an HTTP client with support for keeping a user session between requests.

FastHttpUser uses a different HTTP client (geventhttpclient) compared to HttpUser (python-requests). It’s significantly faster, but not as capable. This client can significantly reduce the resource consuming of the load test.

wait_time attribute:

In addition to the tasks attribute, one should also declare a wait_time method. It’s used to determine for how long a simulated user will wait between executing tasks. Locust comes with a few built in functions that return a few common wait_time methods.

The most common one is between. It’s used to make the simulated users wait a random time between a min and max value after each task execution. Another built in wait time function is constant which return the number specified by wait_time argument. and the last one is constant_pacing which will track the run time of the tasks, and for each time it’s called it will return a wait time that will try to make the total time between task execution equal to the time specified by the wait_time argument. It is very useful when you want to send a load with a specific RPS (requests per second).

Task/TaskSet/SequentialTaskSet

Tasks are somethings that the User will need to do.When a load test is started, an instance of a User class will be created for each simulated user and they will start running within their own green thread. When these users run they pick a random task that they execute, sleeps for awhile, and then picks a new task and so on.

if we were load-testing an auction website – they could do stuff like “loading the start page”, “searching for some product”, “making a bid”, etc.

A TaskSet class is a collection of locust tasks that will be executed much like the tasks declared directly on a User class.

SequentialTaskSet is a TaskSet but its tasks will be executed in the order that they are declared. Weights are ignored for tasks on a SequentialTaskSet class. It is possible to nest SequentialTaskSets within a TaskSet and vice versa.

There are three ways to declare a task: 1 – declared as methods using the @task decorator. 2 – specify method tasks using the tasks attribute. 3 – specify TaskSet using the tasks attribute.

Locust Web UI (Dashboard) / Headless

Once you’ve started Locust using one of the above command lines, you can access Locust Web Interface at http://127.0.0.1:8089.

Fill Number of Users, Hatch Rate and host then start swarming. It will show the statistic of the load running.

If you don’t know what these Statistics attributes such as Median, Percentiles, Average, RPS… about , please refer to this this post, it explains these statistics terms.

you can run locust without the web UI, also known as Headless mode – for example if you want to run it in some automated flow, like a CI server – by using the --headless flag together with -u and -r.

Hatch Rate: the number of users to spawn per second

on_start and on_stop methods

User and TaskSet classes can declare an on_start method and/or on_stop method. A User will call it’s on_start method when it starts running, and it’s on_stop method when it stops running. For a TaskSet, the on_start method is called when a simulated user starts executing that TaskSet, and on_stop is called when the simulated user stops executing that TaskSet (when interrupt() is called, or the user is killed).

test_start and test_stop events

If you need to run some code at the start or stop of a load test, you should use the test_start and test_stop events. You can set up listeners for these events at the module level of your locustfile:

Distributed Mode

Once a single machine isn’t enough to simulate the number of users that you need, Locust supports running load tests distributed across multiple machines.

To do this, you start one instance of Locust in master mode using the --master flag. This is the instance that will be running Locust’s web interface where you start the test and see live statistics. The master node doesn’t simulate any users itself. Instead you have to start one or -most likely-multiple worker Locust nodes using the --worker flag, together with the --master-host (to specify the IP/hostname of the master node).

A common set up is to run a single master on one machine, and then run one worker instance per processor core on the worker machines.

Step Load Mode

If you want to monitor your service performance with gradually increasing user load and probe the max tps that can be achieved, you can run Locust with Step Load enabled with --step-load.

Running Locust in step load mode without the web UI

$ locust -f --headless -u 1000 -r 100 --run-time 1h30m --step-load --step-users 300 --step-time 20m

Leave a comment