MQTT's Retained Messages is a special message mechanism that allows the Broker to persist the latest message for new subscribers to receive.
Concept of Retained Messages
Definition
Retained messages are messages persisted by the Broker. When a new client subscribes to the topic, the Broker immediately sends the retained message to that client.
Purpose
- State Synchronization: New subscribers can immediately get the latest state
- Initialization Data: Provide initial data for newly connected clients
- State Recovery: Help clients quickly recover to the latest state
- Reduce Requests: Avoid clients actively requesting the latest state
How Retained Messages Work
Setting Retained Messages
Set the Retain flag when publishing messages:
shellPUBLISH Packet Parameters: - Topic: Topic name - Payload: Message content - QoS: QoS level - Retain: true (set as retained message)
Storage of Retained Messages
- Storage Location: Broker memory or persistent storage
- Storage Quantity: Only one latest message per topic
- Storage Overwrite: Newly published retained messages overwrite previous ones
Sending Retained Messages
When a client subscribes to a topic:
- Client sends SUBSCRIBE packet
- Broker checks if the topic has a retained message
- If yes, immediately sends the retained message to the client
- Then sends subsequent new messages
Characteristics of Retained Messages
1. One Message Per Topic
- Rule: Only one latest retained message per topic
- Overwrite Mechanism: Newly published retained messages replace previous ones
- Clear Mechanism: Publishing an empty message (empty Payload) can clear retained messages
2. QoS Level
- Inheritance: Retained message QoS level is determined at publish time
- Subscription Limit: QoS level received by subscribers is limited by subscription QoS settings
- QoS Rule: Actual QoS = min(publish QoS, subscribe QoS)
3. Persistence
- Memory Storage: Default storage in memory
- Persistent Storage: Can be configured to persist to disk
- Broker Restart: Persisted retained messages still exist after Broker restart
4. Message Order
- Sending Order: Retained messages are sent before normal messages
- Subscription Timing: Sent only once at subscription time
- Subsequent Messages: Retained messages are not sent repeatedly
Use Cases
1. Device State Synchronization
shellScenario: Temperature Sensor Retained Topic: sensor/123/temperature Retained Message: {"value": 25.5, "unit": "C", "timestamp": 1234567890} New subscriber subscribes to sensor/123/temperature Immediately receives latest temperature value
2. Configuration Information Publishing
shellScenario: Device Configuration Retained Topic: config/device/123 Retained Message: {"mode": "auto", "interval": 60} New device comes online and subscribes to configuration topic Immediately gets latest configuration
3. System Status Broadcasting
shellScenario: System Status Retained Topic: system/status Retained Message: {"status": "running", "version": "1.0.0"} New client subscribes to system status Immediately gets current system status
4. Switch State
shellScenario: Smart Switch Retained Topic: switch/123/state Retained Message: {"state": "on"} New subscriber immediately gets switch state
Code Examples
Python (paho-mqtt)
pythonimport paho.mqtt.client as mqtt import json import time def on_connect(client, userdata, flags, rc): print(f"Connected with result code {rc}") # Subscribe to topic client.subscribe("sensor/+/temperature") def on_message(client, userdata, msg): print(f"Received: {msg.topic} - {msg.payload.decode()}") print(f"Retained: {msg.retain}") # Publish retained message client = mqtt.Client() client.connect("broker.example.com", 1883, 60) # Publish retained message (retain=True) message = {"value": 25.5, "unit": "C", "timestamp": int(time.time())} client.publish("sensor/123/temperature", json.dumps(message), retain=True) # Clear retained message (publish empty message) # client.publish("sensor/123/temperature", "", retain=True) client.disconnect() # Subscriber subscriber = mqtt.Client() subscriber.on_connect = on_connect subscriber.on_message = on_message subscriber.connect("broker.example.com", 1883, 60) subscriber.loop_forever()
JavaScript (MQTT.js)
javascriptconst mqtt = require('mqtt'); // Publish retained message const publisher = mqtt.connect('mqtt://broker.example.com'); publisher.on('connect', () => { console.log('Publisher connected'); // Publish retained message (retain: true) const message = JSON.stringify({ value: 25.5, unit: 'C', timestamp: Date.now() }); publisher.publish('sensor/123/temperature', message, { retain: true }); // Clear retained message (publish empty message) // publisher.publish('sensor/123/temperature', '', { retain: true }); publisher.end(); }); // Subscriber const subscriber = mqtt.connect('mqtt://broker.example.com'); subscriber.on('connect', () => { console.log('Subscriber connected'); subscriber.subscribe('sensor/+/temperature'); }); subscriber.on('message', (topic, message) => { console.log(`Received: ${topic} - ${message.toString()}`); console.log(`Retained: ${message.retain}`); });
Best Practices
1. Retained Message Design
- State Information: Retained messages should represent current state
- Clear and Concise: Message content should be concise and easy to parse
- Include Timestamp: Facilitates judging the age of messages
- Version Control: Can include version information
2. Topic Naming
shellRecommended Format: - sensor/{device_id}/temperature - config/{device_id} - status/{system_id} Avoid Using: - Wildcard topics (cannot publish to wildcard topics) - Overly complex topic structures
3. Message Size
- Limit Size: Retained messages should not be too large
- Recommended Size: Usually less than 1KB
- Broker Limitations: Be aware of Broker limitations on message size
4. QoS Selection
- General State: QoS 0
- Important State: QoS 1
- Critical State: QoS 2
5. Clear Mechanism
- Active Clearing: Publish empty message to clear retained messages
- Regular Cleanup: Regularly check and clean up expired retained messages
- Lifecycle Management: Set reasonable lifecycle for retained messages
Considerations
-
Memory Usage: Retained messages consume Broker memory, large numbers may affect performance
-
Persistence Configuration: If retained messages need to exist after Broker restart, configure persistence
-
Message Updates: Frequent updates to retained messages increase Broker burden
-
Subscription Timing: Retained messages are only sent at subscription time, not repeatedly
-
QoS Limitation: QoS level received by subscribers is limited by subscription QoS settings
-
Empty Message Clearing: Publishing empty message (empty Payload) can clear retained messages
Retained Messages vs Last Will
| Feature | Retained Messages | Last Will |
|---|---|---|
| Trigger Timing | At subscription | At abnormal disconnection |
| Message Source | Publisher sets | Client sets |
| Storage Location | Broker | Broker |
| Recipient | New subscribers | Clients subscribed to that topic |
| Message Quantity | One per topic | One per client |
| Clear Method | Publish empty message | Normal disconnect or reconnect |
Limitations of Retained Messages
- One Per Topic: Each topic can only retain one message, cannot save historical messages
- Memory Usage: Large numbers of retained messages consume significant memory
- Real-time: Retained messages may not be the latest (depends on publish frequency)
- No History: Cannot get historical state changes
- Depends on Broker: Completely relies on Broker reliability
MQTT Retained Messages is a very important mechanism in IoT applications. Proper use can effectively implement state synchronization and initialization, improving user experience and system reliability.