Overview
The publish/subscribe pattern is the foundation of MQTT communication. Publishers send messages to topics without knowing who receives them, while subscribers receive messages from topics they’re interested in. This decoupling enables scalable, flexible IoT architectures.
Topic Structure
MQTT topics are hierarchical strings separated by forward slashes, similar to file paths.
Topic Best Practices
sensors/building1/floor2/temperature
devices/esp32/001/status
vehicles/truck/vin123/location/gps
factory/assembly-line/machine5/alerts
Topics are case-sensitive . sensors/temp and sensors/Temp are different topics.
Topic Wildcards
Subscribers can use wildcards to match multiple topics:
Single Level (+)
Multi Level (#)
Combining Wildcards
The + wildcard matches exactly one topic level: Matches:
sensors/room1/temperature ✓
sensors/room2/temperature ✓
Doesn’t match:
sensors/building1/room1/temperature ✗
sensors/temperature ✗
The # wildcard matches zero or more topic levels and must be the last character: Matches:
sensors/temperature ✓
sensors/room1/temperature ✓
sensors/building1/floor2/humidity ✓
Invalid:
sensors/#/temperature ✗ (# must be last)
You can combine both wildcards: Matches:
sensors/building1/floor1/temp ✓
sensors/building2/floor1/room5/humidity ✓
Doesn’t match:
sensors/building1/floor2/temp ✗
Avoid subscribing to # (all topics) in production as it receives every message on the broker.
Publishing Messages
Python Examples
Simple Publish
Publish with QoS
Continuous Publishing
import paho.mqtt.client as mqtt
import json
import time
client = mqtt.Client( "publisher_001" )
client.connect( "localhost" , 1883 , 60 )
# Publish temperature reading
temperature = 22.5
client.publish( "sensors/temperature" , str (temperature))
# Publish JSON payload
data = {
"sensor_id" : "temp_001" ,
"value" : 22.5 ,
"unit" : "celsius" ,
"timestamp" : time.time()
}
client.publish( "sensors/data" , json.dumps(data))
client.disconnect()
JavaScript Examples
Basic Publishing
JSON Publishing
Periodic Publishing
const mqtt = require ( 'mqtt' );
const client = mqtt . connect ( 'mqtt://localhost:1883' );
client . on ( 'connect' , () => {
console . log ( 'Connected to broker' );
// Publish simple message
client . publish ( 'sensors/temperature' , '22.5' );
// Publish with options
client . publish ( 'devices/status' , 'online' , {
qos: 1 ,
retain: true
}, ( error ) => {
if ( error ) {
console . error ( 'Publish error:' , error );
} else {
console . log ( 'Message published' );
}
});
});
Java Examples
Simple Publish
Async Publishing
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import java.nio.charset.StandardCharsets;
public class SimplePublisher {
public static void main ( String [] args ) {
Mqtt5BlockingClient client = Mqtt5Client . builder ()
. identifier ( "java_publisher" )
. serverHost ( "localhost" )
. serverPort ( 1883 )
. buildBlocking ();
client . connect ();
// Publish message
client . publishWith ()
. topic ( "sensors/temperature" )
. payload ( "22.5" . getBytes ( StandardCharsets . UTF_8 ))
. qos ( MqttQos . AT_LEAST_ONCE )
. send ();
System . out . println ( "Message published" );
client . disconnect ();
}
}
Subscribing to Topics
Python Examples
Basic Subscription
Multiple Subscriptions
Topic-Specific Handlers
import paho.mqtt.client as mqtt
def on_connect ( client , userdata , flags , rc ):
print ( f "Connected with result code { rc } " )
# Subscribe on connect to handle reconnections
client.subscribe( "sensors/temperature" , qos = 1 )
def on_message ( client , userdata , msg ):
print ( f "Topic: { msg.topic } " )
print ( f "Payload: { msg.payload.decode() } " )
print ( f "QoS: { msg.qos } " )
print ( "---" )
client = mqtt.Client( "subscriber_001" )
client.on_connect = on_connect
client.on_message = on_message
client.connect( "localhost" , 1883 , 60 )
client.loop_forever()
JavaScript Examples
Simple Subscription
Wildcard Subscriptions
const mqtt = require ( 'mqtt' );
const client = mqtt . connect ( 'mqtt://localhost:1883' );
client . on ( 'connect' , () => {
console . log ( 'Connected to broker' );
// Subscribe to topic
client . subscribe ( 'sensors/temperature' , { qos: 1 }, ( err ) => {
if ( ! err ) {
console . log ( 'Subscribed successfully' );
}
});
});
client . on ( 'message' , ( topic , payload ) => {
console . log ( `Received message on ${ topic } : ${ payload . toString () } ` );
});
Java Examples
Blocking Subscription
Async Subscription
import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish;
public class BlockingSubscriber {
public static void main ( String [] args ) {
Mqtt5BlockingClient client = Mqtt5Client . builder ()
. identifier ( "java_subscriber" )
. serverHost ( "localhost" )
. buildBlocking ();
client . connect ();
client . subscribeWith ()
. topicFilter ( "sensors/#" )
. qos ( MqttQos . AT_LEAST_ONCE )
. send ();
try ( Mqtt5BlockingClient . Mqtt5Publishes publishes = client . publishes ( MqttQos . AT_LEAST_ONCE )) {
publishes . receive (). ifPresent (publish -> {
String topic = publish . getTopic (). toString ();
String payload = new String ( publish . getPayloadAsBytes ());
System . out . println (topic + ": " + payload);
});
}
}
}
Publish/Subscribe Patterns
One-to-Many
Many-to-One
Request/Response
Single publisher, multiple subscribers: # Publisher
client.publish( "broadcast/news" , "Breaking news update" )
# Subscriber 1, 2, 3... all receive the same message
client.subscribe( "broadcast/news" )
Use cases: Notifications, broadcasts, alertsMultiple publishers, single subscriber: # Publishers send to different topics
client1.publish( "data/sensor1" , data1)
client2.publish( "data/sensor2" , data2)
# Aggregator subscribes to all
client.subscribe( "data/#" )
Use cases: Data aggregation, monitoring dashboardsImplement RPC-style communication: # Requester
client.subscribe( f "response/ { client_id } " )
client.publish( "request/getStatus" , client_id)
# Responder
def on_message ( client , userdata , msg ):
requester_id = msg.payload.decode()
response = get_status()
client.publish( f "response/ { requester_id } " , response)
Best Practices
Publishing:
Use meaningful, hierarchical topic names
Keep payloads small and efficient
Choose appropriate QoS levels
Avoid publishing to $SYS topics (reserved for broker)
Subscribing:
Subscribe in on_connect callback for auto-reconnection
Use wildcards judiciously to avoid message overload
Unsubscribe from topics when no longer needed
Handle malformed messages gracefully
Next Steps
Quality of Service Understand QoS 0, 1, and 2 for reliable delivery
Retained Messages Learn about message retention behavior