2、MQTT协议基础:发布/订阅模型、主题与服务质量

MQTT(Message Queuing Telemetry Transport)是物联网领域最主流的轻量级消息协议,专为低带宽、高延迟、不可靠网络环境下的设备通信而设计。本节将深入剖析MQTT的核心通信模型、主题路由机制以及三种服务质量等级,为后续搭建采集平台打下坚实基础。

2.1 发布/订阅模型

MQTT采用发布/订阅(Pub/Sub)模式,解耦了消息的发送方与接收方。所有消息通过一个中央代理(Broker)进行路由,发布者与订阅者无需直接建立连接,也无需知道对方的存在。

核心优势:发布/订阅模式实现了空间解耦(发布者和订阅者不需要知道对方网络地址)、时间解耦(发布者和订阅者不需要同时在线)以及同步解耦(发布者发送消息后无需等待订阅者处理)。

下图展示了MQTT发布/订阅模型的基本通信流程:

MQTT 发布/订阅模型通信流程 发布者 温度传感器 订阅者 A 数据存储服务 订阅者 B 实时监控看板 订阅者 C 告警推送服务 MQTT Broker 消息代理 / 路由器 发布消息 (主题: sensor/temp) 推送消息 推送消息 说明: 发布者将消息发送到 Broker 的指定主题;Broker 根据主题匹配规则,将消息推送给所有订阅了该主题的订阅者。

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 协议自动处理,应用层无需额外逻辑
性能对比:在同等网络条件下,QoS 0 的吞吐量约为 QoS 1 的 2-3 倍,QoS 2 的 5-10 倍。选择 QoS 等级时,务必根据业务需求权衡可靠性与性能。

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 分别对应至多一次、至少一次、恰好一次的消息传递保证,需根据场景权衡选择。
  • 保留消息让新订阅者立即获取最新状态;遗嘱消息在设备异常断线时自动通知。