跳转到内容

保留消息

本节介绍 MQTT 保留消息(Retained Messages)的工作原理、使用场景和最佳实践。学习完成后,您将能够:

  • 理解保留消息的机制和用途
  • 在新设备订阅时利用保留消息获取最新状态
  • 管理保留消息的生命周期
  • 避免保留消息的常见误用

在开始本节之前,请确保:

  • 理解 MQTT 消息发布机制
  • Mosquitto Broker 已运行
  • 已安装 MQTT 命令行工具

保留消息是 Broker 为每个 Topic 存储的最后一条消息。当有新的订阅者订阅该 Topic 时,Broker 会立即将该消息推送给新订阅者。

没有保留消息:
Publisher New Subscriber
│ │
│─── [发布消息] ──→│ │
│─── [发布消息] ──→│ │
│─── [发布消息] ──→│ │
│ │←── 开始订阅
│ │ 需要等待下一次发布
│ │
│─── [发布消息] ──→│←────── │
有保留消息:
Publisher New Subscriber
│ │
│─── [发布+保留] ─→│ │
│─── [发布+保留] ─→│ │
│─── [发布+保留] ─→│ │
│ │←── 开始订阅
│ │←── 立即收到保留消息
│ │ (最新状态)
Terminal window
# 发布保留消息
mosquitto_pub -h localhost -t "devices/status" \
-r -m '{"device": "ESP32_001", "status": "online"}'
# -r 标志表示这是一个保留消息
# 新设备订阅(随时订阅)
mosquitto_sub -h localhost -t "devices/status" -v
# 立即输出: devices/status {"device": "ESP32_001", "status": "online"}
用例说明示例
设备状态设备在线/离线状态devices/sensor_01/status → "online"
最新传感器值新仪表板立即显示数据sensors/temperature → "26.5"
配置信息设备启动时获取配置config/device_01 → {"interval": 30}
系统状态系统运行状态system/mode → "production"
// ESP32 连接时发布保留消息
void onMQTTConnect() {
// 发布在线状态(保留消息)
mqttClient.publish("devices/esp32_001/status",
"{\"status\":\"online\",\"ip\":\"192.168.1.100\"}",
true); // true = retain
}
// ESP32 断开时(通过遗嘱消息,见 03-14)
// 发布离线状态
// Node-RED: 发布保留的传感器读数
msg.payload = {
device: "ESP32_001",
temperature: 26.5,
humidity: 62,
updated_at: Date.now()
};
msg.retain = true; // 标记为保留消息
return msg;
// Node-RED: 发布设备配置作为保留消息
var config = {
sampling_interval: 10, // 秒
temp_threshold: 30, // °C
mode: "auto"
};
// 发布配置到保留 Topic
node.send({
topic: "config/esp32_001",
payload: config,
retain: true
});

要清除保留消息,向该 Topic 发送一个空 payload 的保留消息:

Terminal window
# 发布空消息清除保留
mosquitto_pub -h localhost -t "devices/sensor_01/status" \
-r -n
# -n 表示发送空消息
# 验证清除
mosquitto_sub -h localhost -t "devices/sensor_01/status" -v
# 如果已是新订阅者,不会收到消息
Terminal window
# 查看所有保留消息(使用特殊的 $SYS Topic)
mosquitto_sub -h localhost -t '$SYS/broker/retained messages/count' -v
# 通过 MQTT Explorer 查看保留消息
# 保留的消息在界面中会显示一个特殊的"保留"标记
# mosquitto.conf - 保留消息配置
persistence true
persistence_location /mosquitto/data/
# 保留消息限制
# 存储所有 Topic 的保留消息在持久化文件中
# 重启后保留消息仍然存在
保留消息的作用范围:
Topic: sensors/temperature
保留消息: "26.5°C"
以下订阅者都会收到保留消息:
sensors/temperature ✅ 完全匹配
sensors/+ ✅ 单级通配符
sensors/# ✅ 多级通配符
# ✅ 全局通配符

每个 Topic 只能保留一条消息。如果重复发布保留消息:

Terminal window
# 第一次发布
mosquitto_pub -h localhost -t "test/retained" -r -m "value1"
# 第二次发布(覆盖)
mosquitto_pub -h localhost -t "test/retained" -r -m "value2"
# 新订阅者只会收到最后一次的值
mosquitto_sub -h localhost -t "test/retained" -v
# 输出: test/retained value2
问题:
如果设备离线后保留消息仍显示"在线",新订阅者会被误导。
解决方案:
1. 结合遗嘱消息在设备离线时自动更新保留消息
2. 在保留消息中包含时间戳,让客户端自行判断时效性
问题:
对所有消息都使用保留标志,导致 Broker 存储膨胀。
解决方案:
只对以下类型的消息使用保留:
✅ 设备状态(在线/离线)
✅ 最新传感器值(可选)
✅ 配置信息
❌ 高频传感器数据(没必要保留全部)
❌ 一次性事件(事件过去后保留无意义)
❌ 日志消息(日志不需要保留)
Terminal window
# 测试步骤 1:发布保留消息
mosquitto_pub -h localhost -t "retained/test" -r -m "This is retained"
# 测试步骤 2:新订阅者立即收到
mosquitto_sub -h localhost -t "retained/test" -v
# 预期立即输出: retained/test This is retained
# 测试步骤 3:覆盖保留消息
mosquitto_pub -h localhost -t "retained/test" -r -m "Updated value"
# 测试步骤 4:清除保留消息
mosquitto_pub -h localhost -t "retained/test" -r -n
# 测试步骤 5:验证已清除
mosquitto_sub -h localhost -t "retained/test" -v
# 预期:没有输出(除非有新发布的消息)
  • 推荐: 设备状态 Topic 使用保留消息
  • 推荐: 保留消息中包含时间戳,便于判断时效性
  • 推荐: 设备离线时通过遗嘱消息清除或更新保留状态
  • 避免: 对高频变化的数据使用保留消息
  • 避免: 保留消息体过大(建议 < 1KB)
  • 避免: 依赖保留消息作为唯一的数据来源

本节要点总结:

  1. 保留消息机制:Broker 存储每个 Topic 的最后一条消息,新订阅者立即收到
  2. 主要用途:设备状态、最新读数、配置信息
  3. 生命周期:发布(含 -r 标志)→ 覆盖 → 清除(空 payload + -r)
  4. 注意事项:过时数据误导、保留消息滥用
  5. 最佳实践:结合遗嘱消息,包含时间戳,合理选择使用场景