• MenuClose
image image 5th February 2019

Load testing and Gatling.io

Recently we’ve been working on a project where the traffic profile is subjected to significant spikes in requests, which typically follow public events. We’re fortunate enough that in most situations we know when such events are going to take place and can carry out capacity planning using Load Testing methods.

The service and architecture which we’re testing is a Java based API, processing a fixed set of ‘POST’ requests from clients, so we’re in a good position that we can also automate the Load Testing scenarios.

We’re under some tight constraints here too; the API must return a response to any POST requests within a 300ms window (including the total round-trip time) and obviously, remain as stable as possible even when we see these surges in traffic.

I’ve played with a number of other Load Testing tools for this project, which included Blazemeter and JMeter and finally ended up at and settled on Gatling.io. I like Gatling mainly because of its ease to script testing scenarios and it also helped us to get the results fast.

At first Blazemeter seemed an appealing option, mainly because it’s a managed and pre-configured testing Platform (choosing between a JMeter or Gatling backend). However, I really struggled to generate the loads which we required. It can also get pricey as you round up the more concurrent user threads, which you need to generate load.

Despite requiring your own test rig server (which we hosted in a MSFT Azure VM), the deployment of Gatling was really straight forward: Ubuntu 18.04, Open JDK 8 and then Gatling 2.3.1 on top! You can get Gatling here.

So once the Gatling test rig is in place, we then needed to configure the test scenarios. It’s fine to start with the ‘BasicSimulation.scala’ (see: ../user-files/simulations/computerdatabase within the extracted Gatling directory). You may also notice that Gatling configuration is based on Scala, but the good news is that you don’t necessarily need to be an expert coder in Scala – it’s simple to follow!

Then you can start to make changes as follows:

Package – use this to specify a custom name for your Load Testing package

class SimulationName extends Simulation { – where SimulationName is stated, you can change this to be specific to the name of your Load Testing project (this appears on the Gatling results dashboard, which you’ll see at the end of test).

val httpConf = http
.baseURL(“https://api.domain.com”) // Here is the root for all relative URLs
.acceptHeader(“text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8”) // Here are the common headers
.doNotTrackHeader(“1”)
.acceptLanguageHeader(“en-US,en;q=0.5”)
.acceptEncodingHeader(“gzip, deflate”)
.userAgentHeader(“Gatling Load Test”)

This is the first bit of key configuration which you point off to your API root (note: you shouldn’t specify any of the API endpoints here). See:
.baseURL(“https://api.domain.com”)

Within val httpconf you can also specify common headers which you may want to pass across in the requests. Above is an example of a few things you can do with these.

val headers_10 = Map(“X-API-Key” -> “xxxxx-xxxxxxxxxxxxxxxxxxxxxxx”, “X-Trace-Id” -> “jfkf0dk”) // Note the headers specific to a given request

val headers_10 is another important section – this is where you will be passing across headers which are specific to the request. Things like an API Key and Trace IDs (whatever your API endpoint requires you to pass through to get a successful response). For the X-Trace-Id header, we also found it useful to pass through a text string, which made it easier to identify the requests from the load test.

val scn = scenario(“First API endpoint Scenario”). // A scenario is a chain of requests and pauses
during(1802) {
exec(http(“matching_service_requests”) // Here’s an example of a POST request
post(“/endpoint1”)
headers(headers_10)
formParam(“firstname”, “name”) // Note the triple double quotes: used in Scala for protecting a whole chain of characters (no need for backslash)
formParam(“lastName”, “name”)
formParam(“dateOfBirth”, “1945-05-29”)
formParam(“email”, “umaury@blanc.org”)
formParam(“postcode”, “xx18 2kl”))
}

In val scn there’s a few different things going on here; this is where we specify how long you wish for the load test to go on for (see during()). In the brackets is where you specify this in seconds. You can also use val scn to specify different sets of testing scenarios – for example: you may have different endpoints and different types of requests accepted by that endpoint. In this set up, you would specify a different scenario name within val scn = scenario(“”).

I mentioned earlier that you don’t specify the endpoint within httpConf, but instead you would specify it within each scenario you create. Above this has been added as post(“/endpoint1”).

You can then use >formParam to specify the parameters which your API endpoint would expect you to pass through, to form a successful request.

Finally...

…but something that you may want to put some thought into before letting the load test run, is the setUp section. Here you can define how many user threads you want to run the load test and the volume of requests per second (RPS). Another useful configuration is that you can choose how many seconds you wish to ramp up the volume of tests over (see: reachRps(2000) in (60 seconds),). That final point is worth considering; in real world usage of your application, traffic would be a gradual increase, rather than a sudden jump in traffic.

setUp(scn.inject(constantUsersPerSec(1300) during (1 seconds))).throttle(
reachRps(20000) in (60 seconds),
holdFor(1740 seconds)
).protocols(httpConf)

In our Load Testing project, the correct use of this part of the configuration was vital to ensure that we could generate the volume of traffic. In this instance, our application on a standard day supports up to 3.6k RPS but in an upcoming major event, we expected to see up to 20k RPS.

A couple of important points before setting this off into the wild:

• Use the setup.throttle configuration wisely; this will control Gatling and ensure that it doesn’t throw too much load at your application! A good approach is to start off with a lower number of constantUsersPerSec and then gradually increase these until you reach the desired volume requests. But also remember to set reachRps correctly.
• If your application is hosted in a public cloud provider; ensure that you check on whether you need to submit a DDoS testing request form. This will stop any auto-scaling functionality which the provider has in place to deal with DDoS attacks. Basically, this will help you avoid a hefty bill!

So that’s it – you’re ready to go. Run ../bin/gatling.sh.

You will see real time feedback of RPS, response times as well as any failed calls (typically HTTP 500 errors).

After the load test completes, Gatling will produce a dashboard of visualisations of the test
results. This will be available at ../
results within the root Gatling directory. The dashboard output is HTML based (use your local browser to render it) – you’re looking for index.html

This basic test setup is good for APIs which expect set parameters to be passed through to form a successful request. However, testing GUI based application such as portals will always be a little more complex. But Gatling has helped us out again and developed a tool called ‘Recorder’. It’s worth a look for this type of load testing project.

Say hello

Want to know more?

Drop us a line – we’re always happy
to chat – we promise we’ll keep the
geek speak to a minimum (unless
that’s your bag in which case we’ll
happily comply)!

Mill One,
Floor Three,
Mabgate Mills,
Leeds LS9 7DZ