核心概念解析:发布/订阅模式、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. 知识体系总览

下面这张图把三个核心概念的关系串起来了。我建议你多看几遍,理解它们之间的依赖关系:

Topic 授权模型核心概念关系图 发布/订阅模式 Topic 层级结构 用 / 分隔,形成树状路径 通配符(+ 和 #) 单层匹配 / 多层匹配 消息按 Topic 路由 订阅匹配依赖通配符 Topic 授权模型 层级决定授权粒度 通配符决定匹配范围 三者关系:发布/订阅模式提供通信框架,Topic 层级结构定义消息地址,通配符实现灵活匹配,共同支撑授权模型

5. 避坑指南:通配符使用中的常见错误

讲了这么多,最后分享几个我亲眼见过的翻车案例:

  1. # 放在中间:factory/#/temperature —— 这是非法的。# 只能出现在 Topic 末尾。
  2. 混淆 + 和 #:有人用 + 想匹配多层,结果发现只匹配了一层。记住:+ 是单层,# 是多层。
  3. 授权规则过于宽松:直接给设备授权 #,结果设备被攻破后,攻击者能订阅所有 Topic。我建议:最小权限原则,能不用 # 就不用。
  4. Topic 层级设计不合理:比如把设备 ID 放在第一层,导致授权规则无法批量管理。我习惯把租户/项目放第一层,区域放第二层,这样授权规则可以按区域批量下发。

一个小技巧:在设计 Topic 层级时,先画一张树状图,标出哪些节点需要独立授权。然后问自己:这个设备需要看到哪些数据?需要操作哪些功能?根据答案决定用 + 还是 #。

好了,核心概念就讲到这里。你把这些搞懂了,后面讲授权策略、ACL 规则、动态权限更新时,就会觉得顺理成章。记住:Topic 结构设计得好,授权就成功了一半。