MQTT主题设计规范:智慧农业设备分层Topic设计(设备/大棚/分组)
Topic 设计是 MQTT 项目中最容易被忽视、却影响最深远的架构决策。改 Topic 结构等于推翻重来——设备端、服务端、前端全得跟着改。本文聊聊怎么设计一套"不后悔"的 Topic 体系。
一、为什么Topic需要设计规范
先看一个真实的反面案例。
某农业项目初期,十几个传感器随意起了 Topic:temp1、greenhouse/humidity、farm_data/soil、sensor_001/data。设备少的时候还能管理,后来扩到 500 个设备,问题全来了:
- 无法批量订阅:Topic 没有层级规律,想监控"所有温度传感器"做不到。
- 通配符订阅失控:有人用
#订阅了全部消息,导致每条消息都推一份给他,流量爆炸。 - 命名冲突:两个开发者各自起名,
pump/status到底是1号泵还是2号泵?没人说得清。 - 权限无法控制:Topic 无规律,ACL(访问控制列表)没法按层级授权。
结论:没有规范的 Topic = 灾难的开始。Topic 设计应该在项目第一天就定好规范,所有开发者共同遵守。
二、Topic设计基本原则
2.1 层级清晰,用/分隔
MQTT Topic 用/作为层级分隔符,类似文件系统路径。每一层代表一个维度的分类。
farm/greenhouse03/sensor/temperature从左到右,范围从大到小:农场 → 大棚 → 设备类型 → 数据类型。
2.2 命名规范
- 小写字母 + 下划线:
greenhouse_03,不用GreenHouse03或green-house-03 - 不用空格和特殊字符:空格、中文、
#、+都是禁忌(#和+是通配符保留字) - 见名知意:
temp不如temperature,dev不如device
2.3 避免开头使用/
# ❌ 不推荐 /agriculture/farm01/sensor/temperature # ✅ 推荐 agriculture/farm01/sensor/temperature以/开头会在第一层产生一个空层(""),虽然 MQTT 5.0 允许,但在某些客户端和 Broker 上可能引发兼容性问题,调试时也容易混淆。
2.4 Topic 不宜过深
建议不超过 5 层。层数太多不仅可读性差,通配符订阅时也容易出错。
# ❌ 太深了 agriculture/farm01/zone03/greenhouse02/area05/sensor/temperature/dht22/value # ✅ 刚好 agriculture/farm01/greenhouse02/sensor/temperature三、智慧农业Topic体系设计
下面是一套经过实践验证的 Topic 分层方案:
3.1 层级定义
| 层级 | 含义 | 示例 |
|---|---|---|
| 第1层 | 业务域 | agriculture |
| 第2层 | 农场编号 | farm01 |
| 第3层 | 大棚编号 | greenhouse03 |
| 第4层 | 设备类型 | sensor/controller/camera |
| 第5层 | 数据子类 | temperature/status/command |
3.2 完整示例
上行数据(设备 → 服务器):
# 温度传感器上报 agriculture/farm01/greenhouse03/sensor/temperature # 土壤湿度传感器上报 agriculture/farm01/greenhouse03/sensor/soil_moisture # 水肥泵状态上报 agriculture/farm01/greenhouse03/controller/pump01/status下行指令(服务器 → 设备):
# 控制水肥泵启停 agriculture/farm01/greenhouse03/controller/pump01/command # 控制卷帘机 agriculture/farm01/greenhouse03/controller/curtain01/command设备状态上报:
# 设备在线/离线/故障 agriculture/farm01/greenhouse03/controller/pump01/status3.3 上行/下行分离设计
在更复杂的场景中,可以在设备类型前增加方向层,明确区分上下行:
# 上报类 agriculture/${farm}/${greenhouse}/up/${device_type}/${data_type} # 指令类 agriculture/${farm}/${greenhouse}/down/${device_type}/${command}这样做的好处是权限控制更清晰——设备只能 publish 到up/,只能 subscribedown/,服务端反之。ACL 规则一条就能搞定。
四、通配符订阅策略
MQTT 通配符是 Topic 设计的灵魂,用好通配符能极大简化订阅逻辑。
4.1 两种通配符
| 通配符 | 含义 | 示例 |
|---|---|---|
+ | 匹配单层 | sensor/+/temperature匹配sensor/a/temperature,不匹配sensor/a/b/temperature |
# | 匹配多层(只能放在末尾) | sensor/#匹配sensor/a/temperature和sensor/a/b/c |
4.2 智慧农业通配符实战
监控所有农场的温度传感器:
agriculture/+/+/sensor/temperature一个订阅就能收到所有大棚的温度数据,不用逐个订阅。
监控某个大棚的一切数据:
agriculture/farm01/greenhouse03/#调试单个大棚时特别有用。
监控所有控制器的状态:
agriculture/+/+/controller/+/status运维大盘只关心设备状态,不关心传感器数据,用这条订阅即可。
4.3 通配符使用注意
#必须独占最后一层,不能写成sensor/#/temp$SYS/开头的系统 Topic 不受通配符匹配(详见下文)- 谨慎使用
#订阅全部消息——在大规模场景下会造成性能问题
五、系统类Topic
EMQX 等 Broker 内置了系统级 Topic,以$SYS/开头,用于获取 Broker 自身运行状态:
$SYS/broker/version # Broker 版本号 $SYS/broker/uptime # 运行时间 $SYS/broker/clients/connected # 当前连接数 $SYS/broker/messages/received # 累计接收消息数注意:$SYS不以/开头,且$开头的 Topic不会被通配符匹配。也就是说#不会匹配到$SYS/#,这是协议设计的保护机制,防止系统消息意外泄露给业务订阅者。
六、Topic数量评估
设计 Topic 时要估算总量,确保 Broker 能扛住。
以一个中型农场为例:
| 类型 | 数量计算 | Topic 数 |
|---|---|---|
| 传感器数据 | 10个大棚 × 10种传感器 | 100 |
| 控制器指令 | 10个大棚 × 5个控制器 | 50 |
| 设备状态 | 150个设备 × 1 | 150 |
| 系统Topic | Broker 内置 | ~20 |
| 合计 | ~320 |
EMQX 单节点轻松处理数十万 Topic,320 个完全不在话下。即使是大型基地上万设备,Topic 数也就几万个,毫无压力。
真正需要关注的不是 Topic 数量,而是消息吞吐速率和订阅关系数量。每个订阅关系会占用内存,大规模场景下需要关注 Dashboard 中的订阅数指标。
七、Topic注册与管理
7.1 是否需要Topic白名单
小规模项目(<500设备):不需要。Topic 规范靠团队约定和代码审查保证即可。
中大规模项目(>500设备):建议引入 Topic 白名单。EMQX 支持 ACL 规则配置,限定设备只能 publish/subscribe 特定模式的 Topic:
# EMQX ACL 配置示例-permission:allowaction:publishtopic:agriculture/farm01/+/sensor/+-permission:allowaction:subscribetopic:agriculture/farm01/+/controller/+/command这样即使设备被篡改,也无法向非法 Topic 发送消息。
7.2 Topic命名文档化
强烈建议在项目初期建立 Topic 字典文档,类似 API 文档:
| Topic | 方向 | Payload格式 | 发布者 | 订阅者 | 说明 |
|---|---|---|---|---|---|
agriculture/{farm}/{gh}/sensor/temperature | 上行 | {"temp": 25.3, "ts": 1704067200} | 传感器 | 数据服务 | 温度上报 |
agriculture/{farm}/{gh}/controller/pump/command | 下行 | {"action":"start","speed":80} | 控制服务 | 控制器 | 泵控制指令 |
有了这份文档,前后端、设备端开发者一目了然,不用猜 Topic 含义。
总结
Topic 设计的核心就一句话:像设计数据库表结构一样设计 Topic。想清楚层级、命名、通配符需求和权限边界,一次定稿,全程受用。千万不要"先随便起个名,后面再改"——后面改的代价远超你想象。