引言
随着物联网(IoT)设备的广泛部署,MQTT(Message Queuing Telemetry Transport)作为一种轻量级的消息协议,因其高效、低带宽占用和易于实现等优点,成为了IoT设备间通信的首选。在MQTT中,主题(Topic)是消息传递的关键概念之一,它定义了消息发布者与订阅者之间的通道。今天我们就来深入探讨一下MQTT中的主题以及如何利用通配符来优化消息过滤。
一、什么是 MQTT 主题
- 在MQTT中,主题是一种层次结构的字符串,用于确定消息的路由路径。每个主题由一个或多个用斜杠(/)分隔的主题级别组成,例如home/livingroom/temperature。发布者将消息发送到特定主题,而订阅者则可以订阅感兴趣的主题以接收消息。主题是区分大小写的,并且不包含空格或其他特殊字符。
主题设计的最佳实践
- 简洁明了:尽量保持主题简洁,避免过长。
- 逻辑清晰:使用有逻辑意义的主题层级帮助识别信息类型或来源。
- 灵活可扩展:考虑未来可能的扩展性,设计足够通用的主题结构。
通配符介绍
为了提高消息订阅的灵活性,MQTT提供了两种类型的通配符:单层通配符(+)和多层通配符(#).
单层通配符(+): 可以用来替换主题中的某一级别。例如,订阅
home/+/temperature
将会匹配home/livingroom/temperature
和home/kitchen/temperature
,但不会匹配home/livingroom/kitchen/temperature
。多层通配符(#): 位于主题过滤器的末尾,可以匹配剩余的所有层级。比如,订阅
home/#
将会匹配home/livingroom/temperature
、home/kitchen/temperature
甚至home/livingroom/kitchen/temperature
。以 $ 开头的主题: 有些MQTT Broker的系统主题就是以
$SYS/
开头的注意:通配符只能用于订阅,不能用于发布消息的主题。
使用通配符的实际应用案例
1、智能家居
假设我们正在构建一个智能家居系统,其中需要监控不同房间的温度、湿度等环境数据。通过合理使用通配符,我们可以轻松地设置一些通用的订阅规则:
- 订阅
home/+/temperature
来获取家中所有房间的温度信息。 - 或者使用
home/livingroom/#
来收集客厅内所有类型的数据。
- 订阅
这种方式不仅简化了代码,还提高了系统的灵活性和可维护性。
2、充电桩
充电桩的上行主题格式为 ocpp/cp/${cid}/notify/${action}
,下行主题格式为 ocpp/cp/${cid}/reply/${action}
。
ocpp/cp/cp001/notify/bootNotification
充电桩上线时向该主题发布上线请求。ocpp/cp/cp001/notify/startTransaction
向该主题发布充电请求。ocpp/cp/cp001/reply/bootNotification
充电桩上线前需订阅该主题接收上线应答。ocpp/cp/cp001/reply/startTransaction
充电桩发起充电请求前需订阅该主题接收充电请求应答。
3、即时消息
chat/user/${user_id}/inbox
一对一聊天:用户上线后订阅该收件箱主题 ,将能接收到好友发送给自己的消息。给好友回复消息时,只需要将该主题的
user_id
换为好友的的 id 即可。chat/group/${group_id}/inbox
群聊:用户加群成功后,可订阅该主题获取对应群组的消息,回复群聊时直接给该主题发布消息即可。
req/user/${user_id}/add
添加好友:可向该主题发布添加好友的申请(
user_id
为对方的 id)。
接收好友请求:用户可订阅该主题(user_id
为自己的 id)接收其他用户发起的好友请求。resp/user/${user_id}/add
接收好友请求的回复:用户添加好友前,需订阅该主题接收请求结果(
user_id
为自己的 id)。
回复好友申请:用户向该主题发送消息表明是否同意好友申请(user_id
为对方的 id)。user/${user_id}/state
用户在线状态:用户可以订阅该主题获取好友的在线状态。
二、MQTT 主题常见问题及解答
1、主题的层级及长度有什么限制吗?
- 答:对于主题的总长度(包括所有层级和斜杠),不同的MQTT代理可能有不同的限制。一些常见的MQTT代理如Mosquitto默认允许的最大主题长度为65535字节。因此,在使用时最好查阅所使用的MQTT代理的相关文档以了解其具体限制。
2、MQTT主题是否区分大小写?
- 答:是的,MQTT主题是区分大小写的。例如,Home/Light和home/light被视为两个不同的主题。
3、MQTT主题的最大长度是多少?
- 答:对于主题的总长度(包括所有层级和斜杠),不同的MQTT代理可能有不同的限制。一些常见的MQTT代理如Mosquitto默认允许的最大主题长度为65535字节。因此,在使用时最好查阅所使用的MQTT代理的相关文档以了解其具体限制
4、通配符可以在发布消息时使用吗?
- 答:不可以,通配符只能用于订阅消息,不能用于发布消息的主题。这意味着你不能向一个包含通配符的主题发布消息。
5、通配符订阅会不会影响性能?
答: 使用通配符订阅确实会增加代理的匹配复杂度,尤其是在大规模连接或高频率消息发布的环境中。虽然现代MQTT Broker优化较好,但在性能敏感场景下仍需谨慎使用。
实际应用案例: 某智慧城市项目中,10万个路灯节点上报状态至各自独立主题如
city/street/light/00001/status
。如果监控中心使用通配符订阅city/street/light/#
来获取所有灯的状态,可能会造成较大的资源消耗。解决方案:
- 对于高频采集的数据,建议使用特定主题分组聚合;
- 或使用边缘计算节点先做本地汇总,再统一上传;
- 对于低频或非关键数据,才考虑通配符订阅。
6、重叠订阅了普通主题和通配符主题时如何接收消息?
- 答:假如客户端同时订阅了 # 和 test 主题,当向 test 主题发送消息时,是否会收到两条重复消息?这取决于 MQTT broker 的实现,例如 EMQX 会为每个匹配的订阅发送消息。但是用户可以使用 MQTT 5.0 中的订阅标识符来区分消息来源,然后在客户端中根据订阅标识符来处理这类重复的消息。
7、常见的 MQTT 主题使用建议有哪些?
- 不建议使用
#
订阅所有主题; - 不建议主题以
/
开头或结尾,例如/chat
或chat/
; - 不建议在主题里添加空格及非 ASCII 特殊字符;
- 同一主题层级内建议使用下划线
_
或横杆-
连接单词(或者使用驼峰命名); - 尽量使用较少的主题层级;
- 当使用通配符时,将唯一值的主题层(例如设备号)越靠近第一层越好。例如,
device/00000001/command/#
比device/command/00000001/#
更好。
8、如何有效地使用单层(+)和多层(#)通配符?
- 答:单层通配符(+)用来匹配单一层级的主题,而多层通配符(#)则必须位于主题过滤器的末尾,用来匹配多个层级的主题。合理使用这些通配符可以帮助更灵活地订阅感兴趣的消息,比如home/+/temperature会匹配任何房间的温度信息,而home/#则会匹配家中的所有信息。
9、如果订阅了一个不存在的主题会发生什么?
- 答:如果你订阅了一个当前没有任何发布者发送消息的主题,那么你将不会收到任何消息,直到有消息被发布到该主题。MQTT代理不会因为你订阅了未使用的主题而返回错误或警告。
10、能否通过主题实现远程控制设备?
答: 当然可以。MQTT支持双向通信,可以通过为主题设计控制指令通道来实现远程控制。
实际应用案例: 智能家居中,灯光控制器订阅如下主题:
1 | home/light/livingroom/control |
用户通过APP向该主题发送控制指令(如开/关、亮度调整等),设备接收到消息后执行对应操作。
示例消息内容:
1 | { |
类似地,还可以为风扇、窗帘、空调等设备设计类似的控制主题结构。
11. 如何防止主题被误用或滥用?
- 答: 可以通过权限管理(ACL)和命名规范来限制谁可以发布或订阅哪些主题。
- 实际应用案例: 在工业物联网平台中,每个工厂车间有各自的命名空间,如:
1 | factory/plantA/line1/data |
系统通过MQTT Broker配置访问控制列表(ACL),确保Plant A的操作人员只能访问plantA相关主题,避免越权访问。
同时,对命令类主题(如 /control
)设置更高权限,防止未经授权的设备发起控制操作。
12、如何用主题实现设备状态反馈?
- 答: 可以为每台设备定义一个“状态”主题,用于反馈其运行状态或错误信息。
- 实际应用案例: 在物流车队管理系统中,每辆车都有一个状态主题,例如:
1 | vehicle/bus001/status |
车载终端定时发布位置、速度、油量、故障码等信息,监控中心订阅这些主题以实时掌握车辆状况。
如果某辆车出现故障,可通过专用主题如:
1 | vehicle/bus001/alert |
发送警报通知,以便快速响应。
三、结语
- 理解和正确使用MQTT的主题与通配符对于开发高效的物联网应用至关重要。它们使得我们可以更智能地管理和筛选来自大量设备的信息流,从而为用户提供更加个性化的服务体验。希望本文能帮助你更好地掌握这些概念,并在实际项目中加以运用。