2、MQTT核心概念精讲:发布/订阅模型、主题(Topic)与通配符、QoS等级详解、会话与遗嘱消息
好,咱们进入第二章。这一章可以说是MQTT的“内功心法”。
你想想看,如果连发布/订阅模型都搞不清楚,后面怎么在ThingsBoard上玩转设备数据?我个人习惯是,先把这些核心概念嚼碎了、咽下去,后面写代码才会顺手。
2.1 发布/订阅模型:告别“点对点”的老思路
传统的HTTP通信,说白了就是“客户端问,服务器答”。你请求一个网页,服务器给你返回HTML。这种模式叫请求/响应模式。
但MQTT不一样。它用的是发布/订阅模型。什么意思呢?
想象一下微信群。你在群里发了一条消息(发布),所有在群里的人都能看到(订阅)。你不需要知道谁在看,看的人也不需要知道你是谁。中间那个“群”,就是MQTT里的Broker(代理服务器)。
核心角色有三个:
- 发布者(Publisher):发送消息的设备。比如温度传感器。
- 订阅者(Subscriber):接收消息的设备。比如你的手机App。
- 代理(Broker):负责中转消息的服务器。比如ThingsBoard。
我在项目中遇到过不少新手,上来就问:“MQTT是不是就是服务器给设备发指令?” 嗯,这话只说对了一半。设备也可以主动给服务器发数据。发布者和订阅者是完全解耦的,它们不需要知道对方的存在。
我的小建议: 刚开始接触时,别把MQTT想得太复杂。你就记住一句话:谁发布,谁订阅,Broker在中间当“快递员”。
2.2 主题(Topic)与通配符:消息的“地址”怎么写?
有了发布/订阅模型,消息怎么找到对应的订阅者呢?靠的就是主题(Topic)。
主题就是一个字符串,用来标识消息的类型。它有点像文件路径,用斜杠 / 分层。
举个例子:
sensor/temperature/room1
sensor/humidity/room1
actuator/light/room2/switch
发布者发消息时,必须指定一个主题。订阅者订阅时,也必须指定一个主题。只有主题匹配上了,消息才会被送达。
这里有个关键点:主题不需要提前创建。你直接发就行,Broker会自动处理。这跟数据库建表不一样,灵活得多。
通配符:一次订阅,通吃全家
如果我想订阅所有房间的温度数据,难道要一个一个写?
不用。MQTT提供了两个通配符:
| 通配符 | 含义 | 示例 |
|---|---|---|
+ |
匹配单层 | sensor/+/room1 匹配 sensor/temperature/room1 和 sensor/humidity/room1 |
# |
匹配多层 | sensor/# 匹配 sensor/temperature/room1 和 sensor/humidity/room2/floor3 |
注意: # 通配符只能放在主题的最后。比如 sensor/#/temp 是无效的。我曾经有个同事,调试了半天发现收不到消息,最后发现是通配符位置写错了。嗯,这种坑踩过一次就记住了。
2.3 QoS等级详解:消息到底能不能丢?
这是MQTT里最容易被忽视,但又最重要的概念。
QoS(Quality of Service,服务质量)决定了消息传递的可靠性。MQTT定义了三个等级:
| QoS等级 | 含义 | 可靠性 | 性能开销 |
|---|---|---|---|
| 0 | 最多发一次(At most once) | 最低,可能丢消息 | 最低 |
| 1 | 至少发一次(At least once) | 中等,不会丢,但可能重复 | 中等 |
| 2 | 恰好发一次(Exactly once) | 最高,不丢不重 | 最高 |
QoS 0:说白了就是“发完拉倒”。Broker收到后不会确认,发送端也不管它到没到。适合那些丢了也无所谓的场景,比如每秒上报一次的温度数据。
QoS 1:发送端会等Broker确认。如果没收到确认,就重发。所以消息至少能到一次,但可能重复。我在项目中用QoS 1做设备状态上报,偶尔会收到重复数据,代码里做一下去重就行。
QoS 2:最严格。发送端和Broker之间会走一个四次握手协议,确保消息恰好到达一次。适合控制指令,比如“打开阀门”。如果重复执行,后果可能很严重。
我的经验: 在ThingsBoard中,设备上报数据用QoS 0就够了。控制指令用QoS 1或2。别一上来就全用QoS 2,性能扛不住的。
2.4 会话与遗嘱消息:设备掉线了怎么办?
物联网设备嘛,掉线是家常便饭。MQTT怎么处理这种情况?
会话(Session)
MQTT的会话分为两种:
- 清理会话(Clean Session = true):每次连接都是全新的,Broker不保存任何历史信息。断开后,所有订阅都失效。
- 持久会话(Clean Session = false):Broker会记住你的订阅。断开后,Broker会帮你缓存未接收的消息。等你下次上线,再推给你。
我建议,对于需要离线消息的设备,比如手机App,用持久会话。对于传感器这种频繁上下线的设备,用清理会话,省资源。
遗嘱消息(Will Message)
这个功能很有意思。设备在连接时,可以预先设置一条“遗嘱”。如果设备非正常断开(比如断电、断网),Broker就会帮它把这条遗嘱发出去。
举个例子:
// 设备连接时设置遗嘱
client.connect({
will: {
topic: 'device/status',
payload: 'offline',
qos: 1,
retain: true
}
});
这样,其他订阅了 device/status 的设备,就能第一时间知道这个设备掉线了。
避坑指南: 我曾经在一个项目中,忘了设置遗嘱消息。结果设备掉线了,监控平台一直显示“在线”。排查了半天才发现,是遗嘱没配。从那以后,我每个设备都会配遗嘱,哪怕只是发个简单的 offline 消息。
好了,这一章的核心概念就讲到这里。下一章,我们开始动手,用MQTT客户端连接ThingsBoard,把理论变成实践。