MQTT 主题怎么设计:分层、通配符与权限边界
主题结构是物联网平台里最早定下、又最难改的东西。本文说明分层顺序该怎么排、通配符订阅为什么会把权限控制变复杂,以及上行下行分开的理由。
- 主题结构出厂后极难改,改一次等于一轮 OTA,值得在项目开始时就定清楚。
- 筛选条件放前面:产品线与租户标识应在设备标识之前,否则无法按租户通配订阅。
- 上行与下行必须分开主题,设备才能被限制为上行只写、下行只读。
- 设备凭据的 ACL 里不应允许通配符,否则一台设备可看到同租户全部数据。
- 保留消息与遥测数据不要混用同一主题,否则新订阅端分不清当前值与历史值。
主题(topic)是 MQTT 里唯一的寻址方式。设备往哪里发、平台订阅什么、谁能看到谁的数据,全部由这一串字符串决定。它的特点是:定下来之后极难改 —— 已经出厂的设备固件里写死了主题,改一次意味着一轮 OTA,而没升级成功的那批设备会直接失联。所以这件事值得在项目开始时多花半天。
分层顺序:把筛选条件放在前面
MQTT 的通配符只能按层级匹配,`+` 匹配一层,`#` 匹配其后所有层。这意味着越靠前的层级,筛选能力越强。常见且好用的顺序是:产品线 / 租户 / 设备标识 / 方向 / 具体消息类型。
- 产品线放最前,因为不同产品的消息结构不同,订阅方通常只关心其中一类;
- 租户标识紧随其后,这是后面做权限隔离的关键,放在深层会让 ACL 规则写不出来;
- 设备标识用出厂唯一编号,不要用客户后来起的名字 —— 名字会改,编号不会;
- 方向(上行 / 下行)单独占一层,理由见下一节;
- 最后才是消息类型,例如遥测、属性上报、事件、命令响应。
一个反例是把设备标识放在最前面。这样做之后,「订阅某个租户的全部设备」就无法用一条通配符表达,只能逐个设备订阅,设备上万之后订阅关系本身就成了负担。
上行与下行必须分开
把设备上报和平台下发放在同一个主题下,会带来一个具体问题:设备既要订阅它、又要发布它,于是 ACL 只能同时给出读写权限。一旦某台设备被攻破,它就可以伪造下发命令给自己,甚至在主题设计不严谨时影响到其他设备。
分开之后权限就清晰了:上行主题设备只写不读,下行主题设备只读不写。这条边界在平台侧是一行 ACL 规则,在安全评审时却是最容易被问到的一条。
通配符订阅与权限的冲突
业务侧常常希望「订阅所有设备的数据」,写法是在租户层之后全部用 `#`。这本身没问题,前提是这个订阅端的身份与设备身份是分开的两套凭据。危险的做法是让设备也能用通配符订阅 —— 那等于任何一台设备都能看到同租户所有设备的数据。
实践上的做法是:设备凭据的 ACL 里不允许出现通配符,主题必须包含它自己的设备标识;服务端应用使用另一套凭据,才允许通配符订阅。MQTT 协议本身不规定权限模型,这部分完全由 Broker 的配置决定,Mosquitto 等实现都提供按客户端与主题模式配置读写权限的能力。
保留消息与遗嘱消息的位置
保留消息(retained)适合放设备的当前状态,让新接入的订阅端立刻拿到最后一次状态,而不必等下一次上报。遗嘱消息(Will)适合放离线通知,设备异常断开时由 Broker 代发。这两者最好各占一个固定的消息类型层级,不要和遥测数据混在同一个主题里 —— 混在一起之后,一条保留的遥测数据会让新订阅端读到一个早已过期的数值,并且分不清它是当前值还是历史值。
预留扩展位
在消息类型这一层留出命名空间,比在整个结构中间插入新层级容易得多。后者会让所有已有订阅失效。我们在做物联网平台解决方案时的习惯是:主题层级一旦确定就写进接口文档并冻结,新增能力通过新增消息类型实现,而不是调整层级顺序。主题结构属于接口契约的一部分,建议和其余接口约定一起纳入平台与系统开发的文档评审。
判断主题设计好不好,有一个简单检验:写出「某租户下所有设备的某一类消息」和「某台设备的全部下行命令」这两条订阅表达式,如果都能用一行写出来,结构基本是对的。
常见问题
MQTT 主题层级多少层合适?
通常五到六层够用:产品线、租户、设备标识、方向、消息类型。层级过少会导致筛选只能在业务代码里做,过多则每条消息都要携带更长的主题字符串,在窄带网络上是实打实的流量成本。关键不是层数,而是把筛选能力强的维度放在前面。
主题里能不能带中文或空格?
协议允许 UTF-8,但不建议。主题会出现在日志、配置文件、ACL 规则和各类客户端工具里,中文与空格在这些环节上容易出现编码与转义问题。用小写英文、数字和连字符最稳妥。
已经上线的项目想改主题结构怎么办?
可行的过渡办法是平台同时订阅新旧两套主题,设备端通过 OTA 逐步切换到新结构,待确认全部设备已升级后再停止订阅旧主题。过渡期可能持续数月,取决于设备在线率;没有升级成功的那批设备必须有单独的处理预案。
参考资料
- MQTT Version 5.0 OASIS Standard · OASIS
- mosquitto.conf man page · Eclipse Mosquitto
- MQTT - The Standard for IoT Messaging · MQTT.org