本节摘要:MQTT 不止"转发消息",还有一层让系统更有状态感的高级机制——保留消息让新订阅者立刻看到最新值,遗嘱帮你把设备意外掉线广而告之,会话与过期决定断线后还能不能续上。承接 3.3 QoS,通往 3.5 的安全与生产。
QoS 解决了"这条消息怎么送达",但物联网还有个温柔的需求:"我刚上线,怎么马上知道设备如今的状态?" 以及**"设备悄悄死掉了,别的人怎么察觉?"** 这些正好是本章三个高级特性分工的地方。
普通消息是"发完即焚",新订阅者加入时只能静等下一次上报。保留消息(Retained)则让 Broker 把指定主题的最新一条留档,任何新订阅者一订阅就立刻收到这条快照。这个特性对"状态类数据"极其好用:温度、开关、电池电量,谁后来都能看到最新一次。
注意:清掉保留消息需要发布一条"空载荷 + RETAIN=1"的消息,Broker 会把该主题的保留状态一并移除。
设备被拔线、断电、信号断连时,没有手机会挥一挥。MQTT 的**遗嘱消息(Last Will)**让客户端在 CONNECT 时先登记一条"如果我不辞而别就替我发出的话"。一旦 Broker 检测到异常断开(比如 Heartbeat 超时),就自动这条遗嘱发到指定主题,通知所有订阅者"这台设备出事了"。
遗嘱只在非正常断开(网络异常、未及时发 DISCONNECT)时才触发;主动正常下线不会触发,避免误报。
遗嘱是在 CONNECT 的可变头里"提前登记"的,而不是即时发布的一条消息——它描述的是"将来若我倒下,替我怎么说"。所以遗嘱要小而稳,别放细节。
MQTT 里的"会话"理解为一段"与 Broker 维系的状态记录",由 Clean Session(3.1.1)或 Session Expiry Interval(5.0)控制:
下表把会话语义与工程影响对起来:
| 会话类型 | 断线后订阅 | 断线期间的投递 | 适合对象 |
|---|---|---|---|
| 临时会话(clean) | 清空 | 丢弃 | 纯遥测、低频设备 |
| 持久会话(persist) | 保留 | 缓冲补投 | 控制终端、关键指令订阅者 |
⚠️ 常见坑:持久会话不等于"无限期"。MQTT 5.0 里 Session Expiry 设得太大会把 Broker 内存吃光,线上爆发性断连会把未投送队列堆成压力测试;要按场景给会话过期设一个现实上限。
真实系统常把它们搭着用:保留消息让设备最新状态可随时查,遗嘱让异常第一时间为人知,持久会话让关键订阅者不因窗口断开丢消息。三者合起来,就把 MQTT 从"会转发的管道"升级成"有记忆、有情感的中间件"。
保留、遗嘱、会话经常被混着记,其实它们管三件完全不同的事。用三张极简对账表把它们钉清楚:
| 机制 | 它回答的问题 | 触发时机 | 谁来触发 |
|---|---|---|---|
| 保留消息 | 新订阅者怎么看到最新值 | 每次订阅成功 | Broker 推存档 |
| 遗嘱 LWT | 设备猝死怎么告警 | 非正常断开 | Broker 替发 |
| 持久会话 | 断线时订阅与消息是否保留 | 每次断重连 | Broker 缓冲 |
再对三个"易混动词"做一次区分:
把这三个动词分别对号——"留档推送 / 预订代发 / 断线记账"——就不会再把它们搅成一锅。
设想一个楼宇的"设备在线看板"要同时呈现三样信息"最新温度、设备是否掉线、掉线期间的读数是否补发"。逐一落手:传感器每次上报都带 RETAIN=1,新打开看板的瞬间温度即刻显示;传感器用遗嘱在 alarming/device_down 登记"某设备异常",掉电几秒内看板就跳出红灯;想看掉线期间漏掉的关键报警,接收端改用持久会话订阅,重连即把欠账补齐。三者各司其职,凑成一套"可查现状 + 可察异常 + 不丢关键"的完整状态视图。
这个场景也提示一个选型直觉:需要"随时查得到最新值"就开保留,需要"秒级察觉设备死亡"就上遗嘱,需要"断线期间不得中断""就设持久会话。 三者不是固定套餐,而是按需求逐项勾选的能力积木。
状态和可靠性都摆平了,下一节给整套机制上锁——TLS、认证授权与生产加固。