一、MQTT 保留消息简介
一、基础概念对比
普通消息:如果消息发布时,对应的主题下没有任何订阅者,MQTT服务器会直接将这条消息丢弃,不会做任何留存。
保留消息:这类消息会被保存在MQTT服务器中,后续任何新的订阅者订阅和该保留消息主题匹配的主题时,都会立刻收到这条消息——哪怕这条消息是在订阅者发起订阅之前就已经发布的。
文档中也通过示意图演示了这个流程:发布者在10:00向主题sensor/t1发布消息,服务器将这条消息存入保留消息存储区;当客户端在11:00订阅sensor/t1主题时,服务器会立刻将这条历史保留消息推送给客户端。
二、典型使用场景
文档中列举了3个非常实用的落地场景:
智能家居状态同步:智能家居设备仅在状态发生变更时才上报数据,控制端上线后可以直接通过保留消息获取设备的最新状态,不需要等待设备下一次状态上报。
传感器数据即时获取:部分传感器上报数据的间隔很长,新的订阅者发起订阅后,不需要等待传感器下一次上报,就能立刻拿到最新的传感器数据。
静态属性分发:传感器的版本号、序列号这类几乎不会变更的属性,设备上线后发布一条保留消息,后续所有订阅该主题的新客户端都可以直接获取到这些属性信息。
MQTT保留消息是MQTT协议中用于解决“新订阅者无法立即获取主题最新状态”问题的核心特性,它和之前介绍的离线消息缓存、持久会话是完全独立的机制,即使发布消息的客户端会话已经结束,保留消息也不会被自动删除。
二、认识MQTT 保留消息
核心定义
保留消息是发布时将Retained标志设置为true的普通MQTT消息,MQTT服务端会为每个主题单独存储最新的一条保留消息。当新的客户端订阅该主题(或匹配该主题的通配符主题)时,服务端会立即将这条保留消息推送给订阅者,无需等待发布者下一次主动发消息。
核心特性
单主题单缓存:每个主题最多仅存储1条保留消息,新发布的同主题保留消息会直接覆盖旧的内容,始终保留该主题的“最后已知有效状态”
独立于会话:保留消息不属于任何客户端的持久会话,发布该消息的客户端断开连接、会话过期销毁后,保留消息依然会保存在服务端
订阅即推送:只有在保留消息发布之后才发起订阅的客户端,才能收到这条保留消息;提前订阅的客户端不会自动收到已存在的保留消息,需要取消订阅后重新订阅才能获取
QoS兼容:保留消息的存储逻辑不受消息QoS等级影响,即使是QoS 0等级的消息,只要设置了Retain标志也会被服务端存储
典型使用场景
智能家居场景:设备仅在状态变更时上报数据,用户打开控制App订阅设备状态主题后,能立刻获取设备当前的在线/离线、开关状态,无需等待设备下一次主动上报
低频次传感器场景:温湿度、定位传感器上报间隔长达数小时,新接入的监控端订阅后可直接拿到最新的采集数据,不用长时间等待
静态属性同步场景:传感器固件版本、设备序列号这类几乎不会变更的属性,发布为保留消息后,后续所有新接入的订阅者都能直接获取,无需重复发布
发布与删除操作
发布保留消息:几乎所有MQTT客户端库都提供了对应参数,发布消息时将retainFlag设置为true即可,比如ESP8266的PubSubClient库中,调用publish(topic, payload, true)就能发布保留消息
删除保留消息:最通用的方式是向目标主题发布一条0字节负载的保留消息,服务端收到后会自动删除该主题下的保留消息;部分服务端还支持在管理后台手动删除,MQTT 5.0版本还可以通过设置消息过期间隔,让保留消息到期后自动销毁
常见配置说明
以主流的EMQX服务端为例,支持灵活配置保留消息的运行规则:
存储方式可选内存存储或内存+磁盘持久化存储,持久化模式下服务端重启后保留消息不会丢失
可设置最大存储条数、单条消息最大负载大小,超出限制的普通保留消息会被当作非保留的普通消息处理
支持配置保留消息派发速率,避免订阅者匹配大量通配符主题时,一次性收到过多保留消息导致内存溢出
三、如何发布和删除MQTT保留消息
发布和删除MQTT保留消息的操作可以分为客户端侧通用配置、主流工具实操演示、服务端侧管理操作三个部分,覆盖不同使用场景的需求:
一、通用发布规则
发布保留消息的核心操作非常简单,在调用MQTT客户端的发布接口时,将消息的Retained标志设置为true即可。
服务端会自动为该主题存储这条最新的保留消息,后续新订阅该主题的客户端会立刻收到这条消息
新发布的同主题保留消息会直接覆盖旧的内容,每个主题最多仅保留1条有效保留消息
保留消息不受QoS等级限制,即使是QoS 0等级的消息,只要设置了Retain标志就会被服务端存储
二、主流工具发布实操
MQTTX 桌面客户端
创建好MQTT连接后,在消息输入区域勾选界面上的「Retain」复选框,填写目标主题和消息内容后点击发送即可完成保留消息发布。
MQTTX CLI 命令行工具
执行以下命令即可发布保留消息,示例中向主题t/1发布指定内容的保留消息:
mqttx pub-t't/1'-m'A retained message from MQTTX CLI'--retaintrue-h'localhost'-p1883Mosquitto 命令行工具
使用mosquitto_pub命令添加-r参数即可发布保留消息:
mosquitto_pub-r-q1-ta/b/c-m'hello'三、删除保留消息的多种方式
客户端主动发布空保留消息
这是协议层面通用的删除方式,向目标主题发布一条Payload长度为0的保留消息,服务端收到后会自动删除该主题下的保留消息。以MQTTX CLI为例,执行以下命令即可完成删除:
mqttx pub-t't/1'-m''--retaintrue-h'localhost'-p1883MQTT 5.0 自动过期删除
在发布保留消息时,通过MQTT 5.0新增的「消息过期间隔」属性设置消息的有效时长,到期后服务端会自动清理这条保留消息,无需手动操作。
服务端管理后台手动删除
以EMQX服务端为例,在Dashboard的「监控 -> 保留消息」页面,可以查看所有已存储的保留消息,直接选中指定主题的消息点击删除即可,还支持通过REST API批量删除保留消息。
服务端配置规则自动清理
可以在服务端配置保留消息的全局有效期,设置后所有未单独指定过期时间的保留消息,达到配置的时长后会被自动回收清理。
三、如何通过REST API批量删除MQTT保留消息
以主流的EMQX MQTT服务端为例,通过REST API批量删除保留消息的完整操作流程如下:
前置准备
确认EMQX服务已开启REST API服务,默认API端口为8081,可在服务端配置文件中确认端口与访问权限设置
提前获取EMQX的API访问凭证,默认管理员账号密码为admin/public,需提前在Dashboard中完成账号配置
核心批量删除操作
EMQX提供了原生的保留消息批量删除API,无需额外编写复杂脚本即可完成全量或指定范围的清理:
删除集群所有保留消息
向EMQX的管理API地址发送DELETE请求,即可一次性清空集群中所有存储的保留消息,示例请求如下:
curl-uadmin:public-XDELETE"http://<你的EMQX服务地址>:8081/api/v5/retained/messages"请求执行成功后会返回操作成功的状态码,集群内所有节点上的保留消息都会被同步清除。
按主题筛选批量删除
如果不需要清空全部保留消息,可先调用GET请求获取所有保留消息的主题列表,通过通配符筛选出需要删除的目标主题,再循环调用单条保留消息删除接口完成批量清理:
第一步:获取全量保留消息列表
curl-uadmin:public"http://<你的EMQX服务地址>:8081/api/v5/retained/messages"返回结果中会包含所有保留消息的主题、QoS、发布时间等完整信息。
第二步:筛选出需要删除的主题后,调用单条删除接口,指定主题URL编码后传入即可删除对应保留消息,可通过脚本循环执行实现批量删除:
curl-uadmin:public-XDELETE"http://<你的EMQX服务地址>:8081/api/v5/retained/messages/<URL编码后的目标主题>"注意事项
执行全量删除前建议先调用GET接口导出保留消息列表做备份,避免误删需要保留的状态数据
如果EMQX开启了保留消息的磁盘持久化存储,API删除操作会同时清理内存和磁盘上的对应数据,无需额外重启服务
部分第三方MQTT服务端的REST API实现可能存在差异,可参考对应服务端的官方API文档调整请求路径和鉴权方式。