MQTT 协议通过多种机制协同工作来保证消息的可靠性,核心是服务质量(QoS)等级和会话持久性。下面分层解析。
1. QoS 等级:端到端的可靠性契约
MQTT 定义了三种 QoS 等级,发布时指定,Broker 根据发布 QoS 和订阅 QoS 的最小值进行转发。
| QoS | 名称 | 行为 | 重复可能 | 可靠性保障 |
|---|---|---|---|---|
| 0 | 至多一次 | 发送即忘,不确认,不重试 | 否 | 无(依赖 TCP 的可靠性,但 TCP 只保证传输,不保证应用层收到) |
| 1 | 至少一次 | 发送方存储消息,等待PUBACK,超时重发 | 是 | 确保消息到达 Broker/接收方,但可能重复 |
| 2 | 恰好一次 | 四步握手:PUBLISH→PUBREC→PUBREL→PUBCOMP | 否 | 确保消息既不丢失也不重复 |
QoS 1 详细流程
发布者 ──PUBLISH(qos=1, packetId=1)──→ Broker 发布者 ←──────────PUBACK(packetId=1)────────── Broker- 发布者本地缓存消息直到收到
PUBACK,超时则重发(仍使用相同的 packetId)。 - Broker 会去重(基于 packetId)避免重复处理。
QoS 2 详细流程
发布者 ──PUBLISH(qos=2, packetId=1)──→ Broker 发布者 ←──────────PUBREC(packetId=1)────────── Broker 发布者 ──PUBREL(packetId=1)──────────→ Broker 发布者 ←──────────PUBCOMP(packetId=1)────────── BrokerPUBREC表示 Broker 已接收并持久化消息。- 发布者收到
PUBREC后才发送PUBREL,Broker 收到PUBREL才真正分发消息并回复PUBCOMP。 - 每个环节都有超时重传和去重机制,保证最终一次且仅一次。
2. 消息标识符(Packet Identifier)与重传
- 每个 QoS 1/2 的消息都带有一个16 位 packetId(每个客户端独立递增)。
- 发送方将消息存入重传队列,启动重传计时器。
- 若未在约定的时间内收到对应的确认报文,则重发原消息(保持原 packetId)。
- 接收方根据 packetId 去重:收到重复的
PUBLISH但已经处理过,则直接回复确认(PUBACK或PUBREC)但不再次处理。
3. 持久会话(Clean Session = false)
客户端连接时可设置cleanSession标志:
- true:每次连接都是全新会话,之前的订阅、离线消息全部丢弃。
- false:持久会话。Broker 保存该客户端的订阅信息以及未确认的 QoS 1/2 消息。
离线消息缓存:
- 当 QoS 1/2 消息发布到 Broker,而目标客户端当前离线时,Broker 会持久化存储这些消息(取决于配置和存储限制)。
- 客户端再次连接(使用相同的 Client ID)后,Broker 立即推送所有积压的离线消息。
- 这保证了网络闪断或设备重启后不会丢失消息。
4. 补充可靠性机制
保留消息(Retained Message)
- 发布者设置
retain = true,Broker 会持久化该主题的最新消息。 - 任何新订阅该主题的客户端(包括之前离线后重新订阅)立即收到这条保留消息,无须等待下一次发布。
- 常用于状态同步(如设备在线/离线标志)。
遗嘱消息(Last Will and Testament, LWT)
- 客户端正常连接时指定遗嘱主题、QoS、内容和
retain标志。 - 若 Broker 检测到客户端非正常断开(如网络超时、崩溃),Broker 自动发布这条遗嘱消息给所有订阅者。
- 这确保了其他设备能及时发现异常离线。
心跳(Keep Alive)
- 客户端与 Broker 协商一个
keepAlive秒数。 - 双方在空闲时定期发送
PINGREQ/PINGRESP。 - 若 Broker 在
1.5 × keepAlive时间内未收到任何报文,则判定客户端异常断开,触发遗嘱消息。
5. Broker 自身的可靠性
虽然标准 MQTT 协议主要定义客户端行为,但生产级 Broker(如 EMQX、Mosquitto、VerneMQ)通过以下机制加强可靠性:
- 消息持久化:将未确认的 QoS 1/2 消息写入磁盘,防止 Broker 重启丢失。
- 集群与数据复制:多节点之间同步会话状态和消息,节点故障时可切换。
- 流量控制:限制发布速率,避免消息堆积溢出。
6. 组合策略:实现不同级别的可靠性
| 场景 | 推荐配置 | 效果 |
|---|---|---|
| 普通传感器数据(允许偶尔丢失) | QoS 0 + cleanSession=true | 最低开销,不保证可靠 |
| 设备控制指令(允许重复,不能丢失) | QoS 1 + cleanSession=false | 至少一次,离线后重连可收到 |
| 计费、关键告警(不许重复,不许丢失) | QoS 2 + cleanSession=false | 恰好一次,最强可靠性 |
| 新设备入网获取最新状态 | 使用保留消息发布设备状态 | 新订阅者立即获得当前值 |
| 监控设备在线状态 | 配置遗嘱消息 | 其他设备能感知离线 |
总结
MQTT 的可靠性不是由单一机制保证,而是QoS 确认 + 消息标识符重传去重 + 持久会话缓存 + 保留/遗嘱 + Broker 存储共同作用。
- QoS 1/2保证了端到端的传输可靠。
- 持久会话保证了离线期间的可靠存储。
- 重传与去重解决了网络丢包和重复。
正确组合这些机制,可以在物联网弱网环境中获得媲美 TCP 的应用层可靠性。