MQTT 协议基础:发布/订阅模型、主题与通配符、QoS 等级、保留消息与遗嘱消息

聊 MQTT,咱们得先搞清楚它到底是个什么路子。说白了,它不是那种「你问我答」的传统通信协议,而是一种发布/订阅模式。我刚开始接触工业物联网那会儿,总觉得这玩意儿跟 HTTP 差不多,后来在产线上踩了坑才明白——完全两码事。

发布/订阅模型:解耦才是王道

传统客户端-服务器模型里,客户端直接跟服务器对话。但 MQTT 不一样,它中间夹了一个Broker(消息代理)。所有设备都不直接通信,而是通过 Broker 中转。

核心角色就三个:

  • Publisher(发布者):只管发消息,不管谁收
  • Subscriber(订阅者):只管收消息,不管谁发
  • Broker(代理):负责消息路由和分发

我在一个智慧工厂项目里遇到过这种情况:现场有 200 多个传感器,每个都要跟中央系统通信。如果用 HTTP,每个传感器都得维护一个长连接,服务器压力巨大。换成 MQTT 后,传感器只管往 Broker 丢数据,中央系统订阅自己关心的主题就行。服务器负载直接降了 60%。

这种模型的好处很明显——发布者和订阅者完全解耦。它们不需要知道对方的存在,不需要同时在线,甚至不需要在同一网段。你想想看,这对工业现场有多重要?设备可能分布在不同的车间、不同的楼层,网络环境千差万别,解耦之后,整个系统的弹性就上来了。

主题(Topic)与通配符:消息的路由规则

主题是 MQTT 消息的路由标签。它不像 IP 地址那样是数字,而是层级结构的字符串,用斜杠 / 分隔。

举个例子:

factory/workshop1/temperature
factory/workshop1/humidity
factory/workshop2/temperature

这种结构的好处是——你可以按车间、按设备类型、按数据维度来组织消息。我习惯把主题设计成 站点/区域/设备类型/数据点 这种格式,这样后期扩展起来特别方便。

通配符:订阅的灵活利器

MQTT 提供了两个通配符,让你可以一次订阅多个主题:

通配符 含义 示例 匹配结果
+ 匹配单层 factory/+/temperature 匹配所有车间的温度数据
# 匹配多层 factory/# 匹配 factory 下所有数据

注意:# 通配符只能放在主题末尾,不能出现在中间。比如 factory/#/temperature 是无效的。我曾经有个同事这么写过,结果 Broker 直接报错,排查了半天才发现是通配符位置不对。

通配符用好了,能大幅减少订阅数量。比如你要监控整个工厂的所有温度数据,只需要订阅 factory/+/temperature 这一个主题就行,不用每个车间都订阅一次。

QoS 等级详解:消息可靠性的三个档次

QoS(Quality of Service)是 MQTT 保证消息可靠性的机制。它分三个等级,每个等级对应不同的开销和可靠性。

QoS 等级 名称 发送次数 确认机制 适用场景
0 至多一次 1 次 无确认 环境温度、湿度等非关键数据
1 至少一次 至少 1 次 PUBACK 确认 设备状态、报警信息
2 恰好一次 2 次握手 PUBREC/PUBREL/PUBCOMP 控制指令、关键参数写入

QoS 0: 发出去就不管了,不确认、不重发。性能最好,但可能丢消息。适合那些丢了也无所谓的场景,比如温度采集,每秒都在更新,丢一两个没关系。

QoS 1: 保证消息至少到达一次,但可能重复。Broker 收到消息后会回复 PUBACK,如果发布者没收到确认,就会重发。我在一个设备状态监控项目里用的就是 QoS 1,因为设备状态变化不频繁,但丢了就麻烦了。

QoS 2: 最可靠,但开销最大。它通过四次握手保证消息不丢不重。适合控制指令,比如「关闭阀门」「启动电机」。我曾经在一条自动化产线上用过 QoS 2 下发启动指令,虽然延迟比 QoS 1 高了 30 毫秒,但保证了指令不会重复执行——重复启动电机可是会烧设备的。

我的建议:不要所有消息都用 QoS 2。工业现场消息量大的时候,QoS 2 的握手开销会拖垮 Broker。我一般遵循「非关键用 QoS 0,状态用 QoS 1,控制用 QoS 2」的原则。

保留消息与遗嘱消息:两个容易被忽略的利器

这两个功能在工业场景里特别实用,但很多人容易忽略。我刚开始用 MQTT 的时候也完全没在意,直到有一次设备掉线后系统完全不知道发生了什么,才意识到它们的重要性。

保留消息(Retained Message)

保留消息是 Broker 上保存的最后一条消息。新订阅者上线时,Broker 会立即把保留消息推送给它。

有什么用?举个例子:

你有一个温度传感器,它每隔 5 秒发布一次温度数据。新上线的监控系统如果等 5 秒才能看到第一个数据,那这 5 秒就是盲区。但如果传感器发布消息时设置了 retain=true,Broker 就会保存最后一条温度数据。新系统一订阅,立刻就能拿到当前温度。

// 发布保留消息示例(伪代码)
client.publish("factory/workshop1/temperature", "25.3", {
  qos: 1,
  retain: true
});

注意:保留消息只保存每个主题的最后一条。如果某个主题不再更新,Broker 会一直保留旧数据。我建议定期清理不需要的保留消息,或者用空消息覆盖掉。

遗嘱消息(Will Message)

遗嘱消息是设备异常断开时,由 Broker 代发的最后一条消息。设备在连接时就要设置好遗嘱主题和内容。

场景:你有一台 PLC,它正常运行时每隔 10 秒发布一次心跳。如果 PLC 突然断电或断网,Broker 检测到连接断开后,就会自动发布遗嘱消息,比如 factory/plc1/status = "offline"。

// 设置遗嘱消息示例(伪代码)
client.connect({
  will: {
    topic: "factory/plc1/status",
    payload: "offline",
    qos: 1,
    retain: true
  }
});

我在一个污水处理厂项目里用过遗嘱消息。现场有 30 多台 PLC,每台都设置了遗嘱。有一次一台 PLC 的网线被老鼠咬断了,中央监控系统立刻收到了离线通知,运维人员 10 分钟就赶到了现场。如果没有遗嘱消息,可能要等到下一次轮询才能发现故障——那会儿污水可能已经溢出来了。

避坑指南:遗嘱消息的 QoS 等级要跟实际场景匹配。如果遗嘱消息丢了,你就永远不知道设备离线了。我一般把遗嘱消息设成 QoS 1 或 QoS 2,确保它一定能到达。

本章知识体系

下面这张图把 MQTT 协议基础的核心逻辑串起来了。你可以看到发布/订阅模型如何运作,主题和通配符怎么路由消息,QoS 等级怎么保证可靠性,以及保留消息和遗嘱消息在什么时机发挥作用。

MQTT 协议基础核心逻辑 MQTT Broker Publisher 发布消息到主题 Subscriber 订阅感兴趣的主题 发布消息 推送消息 主题与通配符 factory/+/temperature factory/# QoS 等级 QoS 0: 至多一次 QoS 1: 至少一次 QoS 2: 恰好一次 保留 & 遗嘱消息 保留: 新订阅者立即获取 遗嘱: 异常断开时代发 发布者与订阅者通过 Broker 解耦 主题路由 + QoS 保障 + 保留/遗嘱 = 工业级消息可靠性

嗯,这一章的内容就这些。MQTT 的基础概念其实不复杂,但每个细节在工业场景里都有它的用武之地。主题设计得好,后期维护就省心;QoS 选得对,系统可靠性就有保障;保留消息和遗嘱消息用好了,能避免很多「设备掉线了都不知道」的尴尬局面。