← 返回笔记列表
MQTT 主题表设计:为什么设备只能发自己的、只能收自己的 ack
设备接入 MQTT 之后,第一个要定的是主题怎么分层、账号怎么分权限。这两个决定一旦定下,固件、后端、Broker 配置都要跟着走,改起来代价很大。
核心约束
系统里只有两种身份连 Broker:
- 设备(ESP32-S3):只发自己的事件与状态,只收自己的 ack;
- 后端:订阅所有设备的事件与状态,向任意设备下发 ack。
家属端不是 Broker 客户端——它只与后端交互,不持有任何 Broker 凭据。
为什么给设备开 elder/bath01/# 的读权限是错的
看起来「设备只能碰自己房间的主题」已经很安全了,但读权限开成通配会带来两个具体问题:
- state 回环:设备自己发布的
elder/bath01/state会被自己收到,白白占用带宽和解析开销; - 泄露面放大:一旦设备账号泄露,攻击者可以读到该设备全部上行内容,而不是只拿到 ack 通道。
所以设备的订阅范围应该收窄到一条:只订阅 elder/<device_id>/ack。
ACL 的实际写法
# 设备账号:只写自己的全部主题,只读自己的 ack
user device-bath01
topic write elder/bath01/#
topic read elder/bath01/ack
# 后端账号:读所有设备的事件与状态,写 ack
user backend-svc
topic read elder/+/event/#
topic read elder/+/state
topic write elder/+/ack
Mosquitto 里
read是订阅(收),write是发布(发)。写反了会出现「设备能发但收不到 ack」这种极难查的故障。
三个连接层参数
| 参数 | 取值 | 理由 |
|---|---|---|
| QoS | 1 | 至少一次,配合 event_id 去重,比 QoS 2 省一半握手 |
| Clean Session | true(两端都是) | 设备侧已自己做断网补传,Broker 再补投会制造第二条路径与重复事件 |
| Keep Alive | 60 s | 超时 1.5 倍即判定掉线(90 s),与 LWT 配合做在线状态 |
连接走 8883/TLS;端口开放、证书与备案期间的过渡方案见部署形态定案。
一个容易忽略的点
Clean Session 全程 true 意味着 Broker 不保存会话。这样一来,设备离线期间的事件只能靠设备自己缓存——这正是 ESP32-S3 上 LittleFS 存在的理由。两条补传路径同时存在,只会带来重复投递。
这篇笔记支撑课题的「通信协议设计」部分:主题分层、ACL 最小权限与连接参数都来自这里,完整课题背景见 非接触式跌倒监测系统。