核心概念解析:发布/订阅模式、Topic层级结构与通配符
好,咱们正式开始聊 Topic 授权模型之前,得先把地基打牢。你想想看,如果连 MQTT 最基本的通信方式都搞不清楚,后面谈权限控制就是空中楼阁。
这一节我会把三个核心概念掰开揉碎了讲:发布/订阅模式、Topic 层级结构,还有那两个让人又爱又恨的通配符(+ 和 #)。我保证,讲完这些你再看设备访问控制,思路会清晰很多。
1. 发布/订阅模式:不是点对点,是“中间人”模式
传统的客户端-服务器通信,比如 HTTP,是客户端直接找服务器要数据。但 MQTT 不一样,它用的是发布/订阅(Pub/Sub)模式。
说白了,就是有个中间人——Broker(代理服务器)。设备之间不直接说话,都通过 Broker 转达。
- 发布者(Publisher):只管把消息扔给 Broker,不管谁收。
- 订阅者(Subscriber):只管告诉 Broker 我想收哪类消息,然后等着。
- Broker:负责收消息、存订阅关系、然后把消息精准推送给订阅者。
我记得刚入行时,有个同事死活不理解:“为啥不直接发?多此一举干嘛?” 后来他负责的项目里设备数量从 10 个涨到 10 万个,点对点连接直接炸了。他才明白——解耦才是 Pub/Sub 的精髓。
核心优势:
- 空间解耦:发布者和订阅者不需要知道对方 IP 或身份。
- 时间解耦:发布者发完消息可以离线,订阅者上线后还能收到(通过持久会话)。
- 速率解耦:发布者每秒发 1000 条,订阅者每秒只能处理 10 条?Broker 帮你缓冲。
在 Topic 授权模型里,我们控制的就是“谁可以发布到哪个 Topic”和“谁可以订阅哪个 Topic”。没有这个模式,授权就无从谈起。
2. Topic 层级结构:像文件路径,但更灵活
Topic 是 MQTT 里消息的“地址”。但它不是扁平的,而是有层级的。
举个例子:
factory/floor1/temperature
factory/floor1/humidity
factory/floor2/temperature
factory/floor2/machine01/status
看到没?用斜杠 / 分隔,就像文件系统的目录。这种结构天然适合做权限的粒度控制。
我个人习惯把 Topic 层级设计成三到四层:
- 第一层:租户/项目标识(如
factory、buildingA) - 第二层:区域/楼层(如
floor1、room201) - 第三层:设备类型或功能(如
temperature、machine01) - 第四层:操作或属性(如
status、command)
我的经验:层级别超过 5 层。太深了 Broker 匹配效率会下降,而且授权规则写起来也痛苦。我见过一个项目 Topic 写了 8 层,最后运维的人想哭。
层级结构还有一个好处:支持批量授权。你不需要给每个设备单独配权限,只要在某一层设好规则,下面的子 Topic 自动继承。嗯,这里要注意——继承规则取决于你用的通配符。
3. 通配符:+ 和 # 的语义与使用场景
通配符是 Topic 授权的灵魂。没有它们,你得给每个 Topic 写一条 ACL 规则,那会疯掉的。
MQTT 只有两个通配符,但用法天差地别:
| 通配符 | 名称 | 匹配范围 | 典型场景 |
|---|---|---|---|
+ |
单层通配符 | 匹配一个层级(且只能是一个) | 订阅某个区域的所有设备 |
# |
多层通配符 | 匹配剩余所有层级(必须放在末尾) | 订阅某个租户的全部数据 |
3.1 单层通配符 +
+ 匹配且仅匹配一个层级。它不能跨层级。
举个例子:
订阅:factory/+/temperature
匹配:factory/floor1/temperature
匹配:factory/floor2/temperature
不匹配:factory/floor1/room1/temperature (因为多了一层)
不匹配:factory/temperature (因为少了一层)
我在项目中遇到过这样一个场景:工厂里有 50 条产线,每条产线有 10 台机器。运维人员只需要看所有产线的温度数据。用 + 一条规则搞定:
允许订阅:factory/productionLine+/temperature
注意,+ 可以出现多次:
factory/+/+/status 匹配 factory/floor1/machine01/status
我曾经踩过的坑:有人把 + 放在 Topic 开头,比如 +/temperature。这在订阅时没问题,但在授权时风险很大——它可能匹配到你不希望暴露的租户数据。我建议:授权规则里尽量少用开头的 +,除非你确定所有租户的数据都可以互相访问。
3.2 多层通配符 #
# 匹配从当前位置到末尾的所有层级。它必须放在 Topic 的最后。
举个例子:
订阅:factory/#
匹配:factory/floor1/temperature
匹配:factory/floor2/machine01/status
匹配:factory/floor1
不匹配:buildingA/floor1/temperature (前缀不同)
# 的威力很大,但也很危险。你想想看,如果给一个设备授权了 factory/#,那它就能看到工厂里所有数据——包括其他产线的商业机密。
我的建议:
- 管理类设备(如总控台):可以用
#,但必须配合身份认证。 - 普通传感器:尽量用
+限定范围,别给#。 - OTA 升级场景:可以用
device/+/ota/#来匹配某个设备的所有 OTA 相关 Topic。
3.3 通配符组合使用
实际项目中,+ 和 # 经常组合使用:
订阅:factory/+/machine/#
匹配:factory/floor1/machine/status
匹配:factory/floor1/machine/command/reboot
匹配:factory/floor2/machine/temperature
不匹配:factory/floor1/sensor/temperature (因为 machine 层级不匹配)
这种组合在授权时特别有用。比如你想让某个维护人员只能操作 1 楼的所有机器,但看不到其他设备:
允许发布:factory/floor1/machine/+/command
允许订阅:factory/floor1/machine/+/status
你看,用 + 锁死楼层,用 # 放开机器下的所有子 Topic,既灵活又安全。
4. 知识体系总览
下面这张图把三个核心概念的关系串起来了。我建议你多看几遍,理解它们之间的依赖关系:
5. 避坑指南:通配符使用中的常见错误
讲了这么多,最后分享几个我亲眼见过的翻车案例:
#放在中间:factory/#/temperature—— 这是非法的。#只能出现在 Topic 末尾。- 混淆
+和#:有人用+想匹配多层,结果发现只匹配了一层。记住:+是单层,#是多层。 - 授权规则过于宽松:直接给设备授权
#,结果设备被攻破后,攻击者能订阅所有 Topic。我建议:最小权限原则,能不用#就不用。 - Topic 层级设计不合理:比如把设备 ID 放在第一层,导致授权规则无法批量管理。我习惯把租户/项目放第一层,区域放第二层,这样授权规则可以按区域批量下发。
一个小技巧:在设计 Topic 层级时,先画一张树状图,标出哪些节点需要独立授权。然后问自己:这个设备需要看到哪些数据?需要操作哪些功能?根据答案决定用 + 还是 #。
好了,核心概念就讲到这里。你把这些搞懂了,后面讲授权策略、ACL 规则、动态权限更新时,就会觉得顺理成章。记住:Topic 结构设计得好,授权就成功了一半。