2、MQTT协议基础:发布/订阅模型、主题与服务质量
MQTT(Message Queuing Telemetry Transport)是物联网领域最主流的轻量级消息协议,专为低带宽、高延迟、不可靠网络环境下的设备通信而设计。本节将深入剖析MQTT的核心通信模型、主题路由机制以及三种服务质量等级,为后续搭建采集平台打下坚实基础。
2.1 发布/订阅模型
MQTT采用发布/订阅(Pub/Sub)模式,解耦了消息的发送方与接收方。所有消息通过一个中央代理(Broker)进行路由,发布者与订阅者无需直接建立连接,也无需知道对方的存在。
下图展示了MQTT发布/订阅模型的基本通信流程:
2.2 主题(Topic)
主题是MQTT消息路由的核心机制,采用层级结构的字符串来标识消息类别。主题类似文件系统的路径,使用正斜杠 / 作为层级分隔符。
2.2.1 主题命名规范
| 规则 | 说明 | 示例 |
|---|---|---|
| 层级分隔 | 使用 / 分隔主题层级 |
factory/floor1/zone2/temperature |
| 单层通配符 | + 匹配一个层级 |
factory/+/zone2/temperature 匹配任意楼层 |
| 多层通配符 | # 匹配剩余所有层级 |
factory/# 匹配 factory 下所有主题 |
| 禁止字符 | 通配符只能用于订阅,不能用于发布 | 发布时主题不能包含 + 或 # |
Sensor/Temp 和 sensor/temp 是两个完全不同的主题。建议在项目初期统一命名规范,全部使用小写字母加连字符或下划线。
2.2.2 主题设计最佳实践
- 层次清晰:按照
设备类型/位置/传感器/数据属性的层级组织主题,便于扩展和维护。 - 避免过深层级:一般不超过 5 层,过深的层级会增加 Broker 的匹配开销。
- 使用有意义的命名:避免使用无意义的缩写,确保团队成员都能理解主题含义。
- 预留版本空间:可在主题开头加入版本号,如
v1/device/...,方便未来协议升级。
2.3 服务质量(QoS)
MQTT定义了三种服务质量等级,用于控制消息传递的可靠性。不同QoS等级在性能与可靠性之间做出权衡,适用于不同的物联网场景。
| QoS 等级 | 名称 | 发送次数 | 确认机制 | 可靠性 | 适用场景 |
|---|---|---|---|---|---|
| 0 | 至多一次 | 1 次 | 无确认 | 最低 | 传感器高频数据(如温度、湿度),允许少量丢失 |
| 1 | 至少一次 | ≥1 次 | PUBACK 确认 | 中等 | 设备状态上报、告警通知,允许重复但不可丢失 |
| 2 | 恰好一次 | 2 次(四步握手) | PUBREC/PUBREL/PUBCOMP | 最高 | 计费指令、关键控制命令,不允许重复或丢失 |
2.3.1 QoS 0:至多一次
消息发送后即丢弃,不等待任何确认。如果网络中断或Broker宕机,消息将丢失。这是MQTT性能最高的等级,适用于对实时性要求高但允许少量数据丢失的场景。
# 使用 paho-mqtt 客户端发布 QoS 0 消息
import paho.mqtt.client as mqtt
client = mqtt.Client()
client.connect("broker.example.com", 1883, 60)
client.publish("sensor/temperature", "25.3", qos=0)
client.disconnect()
2.3.2 QoS 1:至少一次
发布者发送消息后,必须等待Broker返回 PUBACK 确认包。如果超时未收到确认,发布者会重发消息。这保证了消息至少被送达一次,但可能导致订阅者收到重复消息。
# 发布 QoS 1 消息
client.publish("device/status", "online", qos=1)
# 订阅端需要处理消息去重(例如通过消息ID或时间戳)
2.3.3 QoS 2:恰好一次
MQTT最可靠的等级,通过四步握手协议确保消息既不丢失也不重复。虽然开销最大,但在金融计费、工业控制等场景中不可或缺。
# 发布 QoS 2 消息
client.publish("command/actuator", "start", qos=2)
# QoS 2 的完整握手流程由 MQTT 协议自动处理,应用层无需额外逻辑
2.4 保留消息与遗嘱消息
MQTT提供了两种特殊消息机制,用于解决新订阅者获取最新状态和异常断线通知的问题。
2.4.1 保留消息(Retained Message)
当发布者设置 retain=True 时,Broker会持久化该主题的最后一条消息。任何新订阅该主题的客户端会立即收到这条保留消息,无需等待下一次发布。
| 特性 | 说明 |
|---|---|
| 存储位置 | Broker 内存或持久化存储中 |
| 每个主题 | 只保留最新一条保留消息 |
| 清除方式 | 发布一条空消息(payload 为空)且 retain=True |
| 典型用途 | 设备最新状态、系统配置参数、传感器最新读数 |
# 发布保留消息
client.publish("device/status", "online", qos=1, retain=True)
# 清除保留消息
client.publish("device/status", "", qos=1, retain=True)
2.4.2 遗嘱消息(Will Message)
客户端在连接Broker时可以设置遗嘱消息。当客户端非正常断开连接(如网络中断、设备掉电)时,Broker会代其发布这条遗嘱消息,通知其他订阅者该设备已离线。
| 参数 | 说明 | 示例值 |
|---|---|---|
| will_topic | 遗嘱消息发布的主题 | device/status |
| will_payload | 遗嘱消息的内容 | offline |
| will_qos | 遗嘱消息的 QoS 等级 | 1 |
| will_retain | 是否保留遗嘱消息 | True |
# 设置遗嘱消息(在 connect 时配置)
will_topic = "device/status"
will_payload = "offline"
client.will_set(will_topic, will_payload, qos=1, retain=True)
client.connect("broker.example.com", 1883, 60)
# 正常断开连接时,不会触发遗嘱消息
client.disconnect() # 正常断开,Broker 不会发布遗嘱
online 保留消息,同时设置 offline 遗嘱消息。这样新订阅者能立即知道设备状态,异常断线时也能及时感知。
2.5 本章小结
- 发布/订阅模型解耦了消息的生产者和消费者,Broker 负责消息路由。
- 主题使用层级结构组织消息,
+和#通配符提供灵活的订阅能力。 - QoS 0/1/2 分别对应至多一次、至少一次、恰好一次的消息传递保证,需根据场景权衡选择。
- 保留消息让新订阅者立即获取最新状态;遗嘱消息在设备异常断线时自动通知。