Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Stress Test Your Webserver With Gatling

Steps

Create the necessary directories for gatling to operate in:

mkdir -p $HOME/gatling/conf
mkdir -p $HOME/gatling/user-files/simulations
mkdir -p $HOME/gatling/results

Create A Test Configuration

We need to create a "simulation" file for telling Gatling what we want to do in our test. E.g hit our site with 100 users at once.

Create a file at $HOME/gatling/user-files/simulations/MyBasicSimulation.scala with the following contents.

package computerdatabase // 1

import io.gatling.core.Predef._ // 2
import io.gatling.http.Predef._
import scala.concurrent.duration._

class BasicSimulation extends Simulation 
{
  // get the number of users specified, falls back to 1 if not specified
  val numUsers = Integer.getInteger("users", 1)

  // get the URL specified
  val siteUrl = System.getProperty("siteUrl");

  val httpConf = http.baseUrl(siteUrl)
    .inferHtmlResources()
    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    .doNotTrackHeader("1")
    .acceptLanguageHeader("en-US,en;q=0.5")
    .acceptEncodingHeader("gzip, deflate")
    .userAgentHeader("Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0")

  val scn = scenario("MyBasicSimulation")
    .exec(http("request_1")
    .get("/"))
    .pause(5)

  setUp(scn.inject(atOnceUsers(numUsers))).protocols(httpConf)
}

Run The Test!

Now execute the following command after editing the siteUrl and users variables to be the site you wish to hit, and the number of users you wish to hit it with respectively.

docker run \
  -e JAVA_OPTS="-Dusers=20 -DsiteUrl=https://www.mydomain.com/" \
  -it \
  --rm \
  -v $HOME/gatling/conf:/opt/gatling/conf \
  -v $HOME/gatling/user-files:/opt/gatling/user-files \
  -v $HOME/gatling/results:/opt/gatling/results \
  programster/gatling:3.9.5-bullseye

This should automatically pull the image if this is your first time.

I was originally using denvazh/gatling but the image had not been updated in 4 years, so I decided to recreate and publish my own images inspired by the Dockerfiles he had in GitHub. If you wish to build your own Gatling image, or see how it is built, please go visit the repository I used to build the images.

The docker container will run, and eventually ask you to enter a name for your test. Enter a name and leave it to run.

Check Out The Results

When the test finishes, you will be told that the results can be accessed at a location. Obviously this location is wrong as you need to take into account how you mounted the volumes. It will be within your gatling/results volume.

Double click the index.html file and view the results in your browser.

Reduce Verbose Logging Output

The default logging is very verbose, to the point where it is just annoying. I found that the easiest way to get a log level I liked was to create the gatling/conf/logback.xml configuration file with the following contents:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <statusListener class="ch.qos.logback.core.status.NopStatusListener"/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%-5level] %logger{15} - %msg%n%rEx</pattern>
        </encoder>
        <immediateFlush>false</immediateFlush>
    </appender>

    <!-- uncomment and set to DEBUG to log all failing HTTP requests -->
    <!--<logger name="io.gatling.http.engine.response" level="DEBUG" />-->
    <!-- uncomment and set to TRACE to log all HTTP requests -->
    <!--<logger name="io.gatling.http.engine.response" level="TRACE" />-->

    <root level="WARN">
        <appender-ref ref="CONSOLE"/>
    </root>

</configuration>

This XML config file was taken directly from here.

Alternative Test

Another test you may wish to run, is adding x number of users every second for one minute. This can be done with the following simulation code:

package computerdatabase // 1

import io.gatling.core.Predef._ // 2
import io.gatling.http.Predef._
import scala.concurrent.duration._

class SpreadOverOneMinute extends Simulation
{
  // get the number of users to add to the queue per second
  val numUsersPerSecond: Double = System.getProperty("users").toDouble

  // Get the number of seconds to add users for.
  val numSeconds = Integer.getInteger("numSeconds", 1)

  // get the URL specified
  val siteUrl = System.getProperty("siteUrl");

  val httpConf = http.baseUrl(siteUrl)
    .inferHtmlResources()
    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8")
    .doNotTrackHeader("1")
    .acceptLanguageHeader("en-US,en;q=0.5")
    .acceptEncodingHeader("gzip, deflate")
    .userAgentHeader("Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/119.0")

  val scn = scenario(siteUrl)
    .exec(http("request_1")
    .get("/"))
    .pause(5)

  setUp(
    scn.inject(
      constantUsersPerSec(numUsersPerSecond).during(numSeconds)
    )
  ).protocols(httpConf);

}

References{#references}

Last updated: 29th October 2023
First published: 29th October 2019

This blog is created by Stuart Page

I'm a freelance web developer and technology consultant based in Surrey, UK, with over 10 years experience in web development, DevOps, Linux Administration, and IT solutions.

Need support with your infrastructure or web services?

Get in touch