Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/gatling/gatling.io-doc/llms.txt

Use this file to discover all available pages before exploring further.

MQTT is the lightweight, publish-subscribe messaging protocol that powers IoT devices, industrial sensors, connected vehicles, and any system where bandwidth and battery life are at a premium. Because MQTT brokers must handle thousands — sometimes millions — of simultaneous client connections, understanding their performance limits under realistic concurrent load is critical. Gatling’s dedicated MQTT SDK lets you simulate both publishers and subscribers as virtual users, measure delivery latency, and validate that messages are received with the correct Quality of Service level.

MQTT vs HTTP in Gatling

Unlike HTTP, MQTT is a publish-subscribe protocol that does not block threads waiting for a response. Gatling does not record response times for MQTT messages by default. To measure delivery latency, you must use an explicit .expect() check that blocks the virtual user until a matching message is received on the topic. Key metrics to monitor for MQTT load tests are found in the Connections tab of Gatling Enterprise Edition reports, which shows TCP connection open/close rates and concurrent connection counts.

Setting Up the Project

Clone the gatling/gatling-mqtt-demo repository and open the Maven Java project in your IDE. If you are adding MQTT testing to an existing project, add the following imports:
import io.gatling.javaapi.mqtt.*;
import static io.gatling.javaapi.mqtt.MqttDsl.*;

Configure the MQTT Protocol

Point Gatling at your MQTT broker and set the default Quality of Service level:
MqttProtocolBuilder mqttProtocol = mqtt
  .broker("test.mosquitto.org", 1883)
  .qosAtLeastOnce();      // Default QoS for all requests
The public test.mosquitto.org broker is suitable for learning and experimentation. For load tests against your own broker, replace the host and port with your broker’s address. For secure connections, use port 8883 and add .sslContext(...) to the protocol builder.

Write the Subscriber Scenario

The subscriber scenario connects to the broker and listens for messages on a topic. The .expect() call sets a window during which Gatling will wait for a message to arrive and record the delivery time:
ChainBuilder connect = exec(mqtt("Connecting").connect());

ScenarioBuilder subscriberScenario = scenario("MQTT Subscriber")
  .exec(connect)
  .exec(mqtt("Subscribe to Topic")
    .subscribe("gatling/demo/topic")
    .qosExactlyOnce()               // Override default QoS for this step
    .expect(Duration.ofMillis(500)) // Wait up to 500 ms for a message
  );

Write the Publisher Scenario

The publisher connects and sends messages. Adding .expect() here validates that the broker acknowledges receipt within the specified duration:
ScenarioBuilder publisherScenario = scenario("MQTT Publisher")
  .exec(connect)
  .exec(mqtt("Publish to Topic")
    .publish("gatling/demo/topic")
    .message(StringBody("Gatling test message"))
    .expect(Duration.ofMillis(500))
    .check(jsonPath("$.error").notExists())
  );

Define the Injection Profile

In this simulation, one subscriber listens while 1,000 publishers ramp up over 30 seconds — modelling the surge of connected devices that might come online simultaneously:
{
  setUp(
    subscriberScenario.injectOpen(atOnceUsers(1)),
    publisherScenario.injectOpen(rampUsers(1000).during(30))
  ).protocols(mqttProtocol);
}

Complete Simulation

package io.gatling.mqtt.demo;

import io.gatling.javaapi.core.*;
import io.gatling.javaapi.mqtt.*;

import java.time.Duration;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.mqtt.MqttDsl.*;

public class MqttSimulation extends Simulation {

  MqttProtocolBuilder mqttProtocol = mqtt
    .broker("test.mosquitto.org", 1883)
    .qosAtLeastOnce();

  ChainBuilder connect = exec(mqtt("Connecting").connect());

  ScenarioBuilder subscriberScenario = scenario("MQTT Subscriber")
    .exec(connect)
    .exec(mqtt("Subscribing_Topic")
      .subscribe("gatling/demo/topic")
      .qosExactlyOnce()
      .expect(Duration.ofMillis(500)));

  ScenarioBuilder publisherScenario = scenario("MQTT Publisher")
    .exec(connect)
    .exec(mqtt("Publishing_Topic")
      .publish("gatling/demo/topic")
      .message(StringBody("Gatling test message"))
      .expect(Duration.ofMillis(500))
      .check(jsonPath("$.error").notExists()));

  {
    setUp(
      subscriberScenario.injectOpen(atOnceUsers(1)),
      publisherScenario.injectOpen(rampUsers(1000).during(30))
    ).protocols(mqttProtocol);
  }
}

Adding Assertions

Add assertions to your setUp block to set clear pass/fail criteria for the test:
setUp(...)
  .protocols(mqttProtocol)
  .assertions(
    global().failedRequests().percent().lt(1.0),          // < 1% errors
    global().responseTime().percentile(95).lt(500)        // p95 < 500 ms
  );

Running on Gatling Enterprise Edition

Upload your simulation to Gatling Enterprise Edition to run distributed load tests across multiple regions simultaneously. This is particularly important for IoT load tests, where you may want to simulate devices connecting from geographically diverse locations.

Private Locations

If your MQTT broker is inside a private network, deploy Gatling load generators on your own infrastructure using the Private Locations feature.

Dedicated IP Addresses

If your broker is public but protected by a firewall, use Gatling Enterprise Edition’s Dedicated IP Addresses feature to whitelist the load generator IPs.

QoS Level Reference

LevelConstantGuaranteeUse case
0qosAtMostOnce()At most once (fire-and-forget)Telemetry where loss is acceptable
1qosAtLeastOnce()At least once (may duplicate)Most IoT sensor data
2qosExactlyOnce()Exactly onceFinancial or critical commands

Further Reading

Build docs developers (and LLMs) love