news 2026/9/9 20:41:48

MQTT系统主题$SYS全解析:从原理到监控实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MQTT系统主题$SYS全解析:从原理到监控实践

做物联网这几年,MQTT算是我打交道最多的协议。不管你是搞嵌入式、写后端,还是做上位机,只要你的项目里出现过设备上报、指令下发,大概率都绕不开它。而说到MQTT,有一个特别容易被人忽略、却又特别重要的东西,就是系统主题。我记得第一次在Mosquitto的日志里看到一长串以$SYS开头的主题时,还以为是哪个设备发上来的业务数据,查了半天才发现,这是broker自己在“汇报工作”。

今天这篇就专门聊MQTT的系统主题,也就是以$SYS开头的这类特殊主题。它和普通业务主题有本质区别,但很多人不知道它的存在,或者知道了也只是听说过,并不清楚它能干什么、怎么用。我会把原理、常见用法、实操步骤和踩坑经验一次性讲清楚。无论你是刚接触MQTT的新手,还是已经在生产环境里跑着几百台设备的老手,这篇文章应该都能给你一些参考,尤其是那些正准备搭建MQTT服务器、或者正在做设备监控方案的兄弟,建议仔细看看。

1. MQTT系统主题到底是什么:$前缀背后的身份差异

1.1 系统主题的“特殊身份”

先抛一个很多人实测踩过的现象:你用一个客户端订阅#通配符,本意是“把所有消息都收下来”,结果发现$SYS开头的主题一条都收不到。为什么?因为MQTT协议规范里明确规定了,以$字符开头的主题名称,不会被通配符#+匹配

这条规则的核心原因就是:$前缀给系统主题开辟了一块“保护区”。broker希望自己维护的这种内部状态主题,必须由订阅方显式指定才能拿到,而不是谁随便用一个#就能把所有东西一股脑订阅走。你要是真想订阅系统主题,必须显式地订阅$SYS/#这个过滤器,或者写具体的完整主题路径,比如$SYS/broker/uptime

这里的$SYS就是一个“系统命名空间”。它本身不是一个主题,而是一个主题层级的前缀。broker会把运行指标、客户端连接数、消息统计这类信息,实时或者定时地发布到$SYS/下面的各个具体主题上。任何普通客户端只要权限允许,都可以去订阅这些主题,从而获取broker的运行状态。换句话说,$SYS是MQTT服务器自己对外暴露状态的一扇窗户。

1.2 系统主题与普通主题的本质区别

普通业务主题,比如devices/001/temperature,是设备或应用自己决定的,内容是业务数据,发布频率和格式完全由你的系统设计决定。而系统主题正好相反:主题路径由broker实现决定,内容由broker自动生成,发布节奏也由broker控制。你没法往$SYS里随便塞数据,因为那是broker的地盘。

另一个重要区别是保留消息(retained message)的使用。系统主题里的很多指标,比如$SYS/broker/version$SYS/broker/uptime,通常会被设置成保留消息。这样新订阅的客户端一上来就能立刻收到当前值,而不是像普通消息那样要等下一个发布周期。这个设计对监控场景特别友好,后面讲实操的时候我还会再提。

还有一点要注意:$SYS并不是协议强制要求每个broker都实现的主题,它更多是事实标准。市面上主流的MQTT broker,比如Mosquitto、EMQX,基本都会提供一套类似的$SYS主题树,但具体路径和字段会有差异。所以你在网上查资料时,看到不同broker的$SYS例子对不上号,别慌,这是正常的。

1.3 系统主题都包含哪些信息

以最常见的Mosquitto为例,它的$SYS主题树大概包含这么几类信息:

主题含义
$SYS/broker/versionbroker版本号
$SYS/broker/uptimebroker启动后运行了多少秒
$SYS/broker/timestampbroker当前系统时间戳
$SYS/broker/clients/connected当前已连接的客户端数量
$SYS/broker/clients/disconnected当前处于离线状态但保留了会话的客户端数量
$SYS/broker/clients/total曾连接过的客户端总数(含已离线)
$SYS/broker/clients/maximum历史最大同时连接数
$SYS/broker/messages/receivedbroker累计收到的消息数
$SYS/broker/messages/sentbroker累计发送的消息数
$SYS/broker/messages/dropped被丢弃的消息数
$SYS/broker/messages/retained/count当前保留消息的数量
$SYS/broker/subscriptions/count当前有效的订阅关系数量
$SYS/broker/bytes/received累计接收字节数
$SYS/broker/bytes/sent累计发送字节数
$SYS/broker/load/messages/received/1min最近1分钟平均每秒收到的消息数

这些主题的取值基本都是字符串形式的数字或文本,比如$SYS/broker/clients/connected的值可能是42$SYS/broker/uptime的值可能是12345 seconds。你直接用普通的MQTT客户端订阅就能读出来,不需要额外认证,前提是broker没有针对$SYS做ACL限制。

2. 为什么监控MQTT服务器一定要盯系统主题

2.1 broker运行状态:看它是不是还活着

做设备接入久了你会发现,很多“设备不上报”的问题,根子不在设备端,而在broker端。broker一旦假死、卡顿或者悄悄重启了,所有设备都会跟着掉线。这时候如果你能第一时间看到$SYS/broker/uptime的值,就能立刻判断broker是不是刚重启过——uptime一下子变成一个很小的数字,基本就可以断定broker重启了。

同样的道理,$SYS/broker/version可以用来确认你线上跑的到底是哪个版本。跨版本升级之后,如果这个主题的值没有变成新版本,说明你的升级根本没生效,或者启动时加载的配置文件不对。这类问题用业务日志排查反而慢,看系统主题一眼就明白了。

$SYS/broker/load/messages/received/1min这类指标,能反映broker当前的消息吞吐压力。如果这个值长期接近broker的极限,再加上$SYS/broker/messages/dropped持续增长,说明你的架构很可能需要横向扩容,或者业务侧消息量已经超过单节点能力了。有了这些数据,你在跟团队讨论扩容方案时就不是拍脑袋,而是拿数字说话。

2.2 连接与消息统计:判断设备在线率和链路质量

设备在线率是物联网项目里最核心的运维指标之一。$SYS/broker/clients/connected能直接告诉你当前有多少个客户端连接着broker。如果你们采购了1000台设备,配置的期望连接数是800,但这个主题的值长期只有300,那就说明有很大一部分设备没连上来,问题大概率在网络侧或者设备固件侧。

$SYS/broker/clients/disconnected这里要特别说明一下,它统计的并不是普通设备,而是“持久会话(persistent session)”的客户端。这类客户端即使离线,broker也会为它保留会话状态。所以这个值高不一定代表设备有问题,也可能是有大量客户端设置了cleanSession=false但迟迟不重连。这种情况如果积累太多,会占住broker的会话资源。

再配合$SYS/broker/messages/receivedsent的差值,还能看出broker有没有在“丢消息”。正常情况下,收到的消息和发送的消息总量应该基本相当,如果received远远大于sent,就要警惕是不是有大量订阅关系不匹配、或者有消息因为权限问题被丢弃了。这类问题光靠业务日志很难发现,但系统主题会把趋势直接摆在你面前。

2.3 $share共享订阅:另一个容易被忽视的系统前缀

聊系统主题,很多人只记得$SYS,却忘了还有一个以$开头的特殊前缀:$share。它和$SYS性质不太一样,它不是broker发布状态,而是给订阅方用的“共享订阅”语法。

比如说,你的后台有3个消费者实例同时订阅devices/+/data,如果直接用普通订阅,一条设备消息会被3个实例同时收到,这在“每个设备消息只需要一个消费者处理”的场景下就是重复消费。解决办法就是改成共享订阅:$share/consumer_group/devices/+/data。三个实例都订阅这个过滤器,broker会把消息在它们之间做负载均衡,每条消息只推给其中一个实例。

为什么我要在讲系统主题时提它?因为很多人设计Topic时,看到$开头的主题就当成系统主题,容易搞混。其实$share是订阅侧的语法,不是真正的主题名,它只影响broker的派发逻辑。你在做MQTT topic设计规范时,最好先把$SYS$share这类前缀和普通业务主题在文档里明确区分开,不然团队协作时很容易踩坑。

3. 实操:从搭建到采集,完整跑通系统主题

3.1 先搭一个带$SYS的MQTT服务器

实操部分,我用Mosquitto来做示例,因为它轻量、配置简单,在Linux上一条命令就能装好,也非常适合用来验证系统主题的各个细节。当然,用Docker就更省事了。下面这条命令就能起一个最基本的broker实例:

docker run -d --name mqtt-broker -p 1883:1883 eclipse-mosquitto:2

如果是裸机安装,Ubuntu/Debian系可以直接:

sudo apt update sudo apt install -y mosquitto mosquitto-clients sudo systemctl status mosquitto

默认情况下,Mosquitto会每隔10秒钟发布一次$SYS主题的数据。这个间隔可以通过配置文件里的sys_interval参数控制,单位是秒。如果设成0,就表示完全关闭$SYS发布功能。配置文件一般位于/etc/mosquitto/mosquitto.conf,你需要在里面加上类似这样的配置:

listener 1883 allow_anonymous true sys_interval 10

sys_interval不要设得太小,否则broker每次发布系统主题都要额外消耗资源和带宽;也不要设得太大,不然监控数据就会不够实时。我自己一般习惯设成10到30秒。

3.2 用命令行工具订阅系统主题验证

装好客户端工具后,直接在终端订阅$SYS/#,就能把broker当前发布的所有系统主题都打出来:

mosquitto_sub -h 127.0.0.1 -p 1883 -t '$SYS/#' -v

注意这里的-v参数,它会在输出里带上主题名,否则默认只打印消息内容。运行后等几秒钟,你就能看到类似这样的输出:

$SYS/broker/version mosquitto version 2.0.18 $SYS/broker/uptime 86 seconds $SYS/broker/clients/connected 1 $SYS/broker/clients/total 1 $SYS/broker/messages/received 2 $SYS/broker/messages/sent 2

如果你想单独验证某个指标,可以直接订阅完整主题名:

mosquitto_sub -h 127.0.0.1 -t '$SYS/broker/uptime' -v

这时候再用另一个终端发布几条普通消息,比如:

mosquitto_pub -h 127.0.0.1 -t 'test/topic' -m 'hello mqtt'

回到订阅$SYS/broker/messages/received的那个终端,你就会发现收到的消息数增长上去了。这一步能非常直观地让你理解系统主题和业务消息之间的关系。

3.3 用paho-mqtt做定时采集与入库

命令行只能看个直观效果,真要放到生产环境当监控用,还是得写程序去采集。这里我用Python的paho-mqtt库写一个最精简的采集示例,把几个关键系统指标订阅回来,然后打进日志或者时序数据库。

import time import paho.mqtt.client as mqtt TOPICS = [ "$SYS/broker/version", "$SYS/broker/uptime", "$SYS/broker/clients/connected", "$SYS/broker/clients/total", "$SYS/broker/messages/received", "$SYS/broker/messages/sent", "$SYS/broker/messages/dropped", "$SYS/broker/subscriptions/count", ] results = {} def on_connect(client, userdata, flags, reason_code, properties=None): if reason_code == 0: for t in TOPICS: client.subscribe(t) print("connected and subscribed") else: print("connect failed, reason_code =", reason_code) def on_message(client, userdata, msg): key = msg.topic.replace("$SYS/broker/", "") results[key] = msg.payload.decode() print(msg.topic, "->", results[key]) client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) client.on_connect = on_connect client.on_message = on_message client.connect("127.0.0.1", 1883, 60) client.loop_forever()

需要注意,paho-mqtt不同版本对回调函数的签名要求不完全一样,新版建议显式传入CallbackAPIVersion.VERSION2,回调里多了reason_code字段。你跑上面的代码,如果用的是2.x版本,应该能正常输出。

实际项目中,我不会直接print,而是把解析后的结果写入InfluxDB、Prometheus或者直接用Python内置的sqlite3落本地库。核心思路都是一样的:订阅$SYS相关主题,解析数值型字段,按时间戳存储,再配合定时任务做聚合告警。

3.4 把系统主题变成监控面板的数据源

采集到数据后,最常用的做法是接入Grafana这类可视化工具,把clients/connectedmessages/receivedbytes/sent这些指标画成曲线。如果你用的采集程序直接写了InfluxDB,给Grafana配上数据源,几分钟就能出一个实时监控面板。

我自己的习惯是保留三张核心图:第一张是连接数变化曲线,重点看有没有设备批量掉线的毛刺;第二张是消息收发速率,重点看有没有流量突增导致的堆积;第三张是uptime,用于快速识别broker重启事件。这三张图基本覆盖了日常运维里90%的broker健康检查需求。

另外,系统主题也能用来做“存活探测”。如果你的监控程序本身就是个MQTT客户端,那它可以定时订阅$SYS/broker/uptime,一旦超时收不到消息,就说明broker可能挂了或者网络不通。这比单纯ping IP要靠谱,因为它是从MQTT协议层面验证的,能排除“端口通但broker进程卡死”的假活情况。

4. 踩坑实录:系统主题使用的常见问题与排查技巧

4.1 通配符#为什么订阅不到$SYS

这个问题我几乎每次带新人都会遇到。你明明订阅了#,想着把所有消息都收到,结果$SYS一个都没进来。这不是broker坏了,而是MQTT协议规范里明确要求的:以$开头的主题名,不能用#+这种从第一层开始就用通配符的过滤器去匹配。你只能通过显式包含$SYS字样的过滤器去订阅,比如$SYS/#

排查方法也很简单,先确认你的订阅过滤器是不是写了$SYS/#,再看broker有没有开启sys_interval。如果两者都正常,那就看是不是broker本身的$SYS主题名前缀和你预期的不一样。比如EMQX集群环境里,系统主题前缀可能是$SYS/brokers/${node}/...,不是简简单单的$SYS/broker/...,这个要尤其注意。

4.2 sys_interval设太大或直接禁用了

生产环境里,有时候你会发现明明代码是对的,但怎么都收不到系统主题数据。这时候就要去broker配置文件里查sys_interval这个参数。很多团队为了追求性能,会把sys_interval设成0,变相关闭了$SYS发布。这是完全合法的配置,但它会直接让你的监控方案失效。

我自己遇到过一种情况是,线上的broker配置了多个listener,但sys_interval只在其中一个listener上生效,导致另一个端口上的客户端订阅不到$SYS。所以排查的时候,不光要看sys_interval有没有设成0,还要确认你连接的listener和配置的listener是不是同一个。类似这种问题,用mosquitto_sub -t '$SYS/#' -v在目标端口上先跑一遍,是最快的验证方式。

4.3 权限控制没做,把内部信息全裸奔出去了

系统主题里包含着broker版本、连接数、消息量这些内部信息。如果你的broker允许匿名访问,那任何人都能订阅$SYS/#,把你服务端的运行情况摸得一清二楚。这在公网环境里等于把底裤亮给人家看。轻则暴露技术栈,重则给攻击者提供判断服务负载、寻找漏洞版本的线索。

所以,生产环境的broker一定要配置ACL。最简单的做法是:给监控程序单独开一个只读账号,只允许它订阅$SYS/#,业务账号一律禁止触碰系统主题。Mosquitto的ACL文件里可以这样写:

user monitor topic read $SYS/#

然后在mosquitto.conf里启用这个ACL文件:

acl_file /etc/mosquitto/acl.file

用了ACL之后,监控程序用monitor账号连接,业务设备用各自的业务账号连接,两边互不干扰。这是个很小的改动,但能避免很多安全上的尴尬。

4.4 业务Topic用$SYS开头,给自己埋雷

有些项目早期没规范,开发者图方便,把业务主题直接命名为$SYS/devices/001/data,想着“这样看起来有系统感”。结果一发到线上就抓瞎:要么订阅通配符收不到,要么跟broker自身的系统主题冲突,甚至有些broker会直接拒绝客户端往$SYS前缀发布消息。

我的建议很简单,业务主题设计规范里必须写死一条:任何业务主题都不得以$开头$前缀是系统保留区,普通设备消息请用类似iot/{projectId}/{deviceType}/{deviceId}/data这样的结构。这样既不会和系统主题冲突,也能让日志和权限管理更清晰。如果你在定topic规范,可以顺便在文档里加一节“保留前缀说明”,把$SYS$share写进去,后面团队协作会少很多沟通成本。

4.5 不同broker的系统主题差异

最后说一个很容易让人困惑的点:$SYS主题在不同broker实现里并不完全统一。Mosquitto用的是$SYS/broker/...这种扁平结构;EMQX在集群模式下会在第二层带节点名,比如$SYS/brokers/emqx@127.0.0.1/...,而且它的指标维度比Mosquitto多不少。还有一些云厂商的托管MQTT服务,可能直接不开放$SYS,或者把相关指标只通过HTTP API暴露。

所以,网上搜到的$SYS主题清单,不一定能直接套到你的broker上。最靠谱的做法是,拿到一个broker实例后,先用mosquitto_sub -t '$SYS/#' -v把当前实际存在的主题全部拉出来看一眼,以现场结果为准。这一点在做跨平台迁移时特别重要,不要想当然地以为换了个broker,监控脚本还能原封不动跑通。

5. 给初学者的几个实用建议

如果你刚开始做MQTT相关的东西,我建议你先别急着把$SYS当成监控的银弹。先把普通订阅、发布、保留消息、遗嘱消息这几个基础概念玩熟,再回来碰系统主题会顺很多。因为系统主题本质上就是一批“特殊的保留消息”,你对MQTT消息机制理解越深,就越能看懂$SYS的数据到底是怎么来的。

另外,在我的实际经验里,$SYS是排查问题的一把好手,但它不是唯一的手段。broker自身的日志、客户端日志、网络抓包,这三样东西配合着看,才是一个完整的排查链路。系统主题告诉你“发生了什么”,日志和抓包告诉你“为什么发生”,两边的信息合起来,问题定位速度能快不少。

最后再分享一个小技巧:给$SYS/broker/uptime单独配一个监控规则,只要这个主题连续两到三个采集周期没有新消息,就触发告警。这条规则能抓住很多“broker假死但TCP连接还在”的诡异问题。我后来发现,这类问题用业务层心跳反而很难察觉,因为TCP长连接没有断开,应用层也不报错,就是消息不流动。而系统主题一旦停更,问题就立刻暴露了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 20:40:10

物流大数据分析平台全解析:从Hadoop到机器学习预测的完整实践

每年毕业季都会被同一个问题轰炸:“大数据方向毕设到底选什么题?”我的答案一直是——做一个物流大数据分析平台。不是因为物流概念火,而是这个题目能把Hadoop、Spark、Hive、机器学习、深度学习一整条大数据技术链全部串起来,既有…

作者头像 李华
网站建设 2026/9/9 20:38:19

华为耳机单耳补配指南:丢一只不再买全套

丢耳机这事,只有真丢过的人才懂有多崩溃。尤其是TWS真无线耳机,早上出门还好好的,到公司一摸耳朵发现右耳空了,翻遍包和口袋都没有,那一刻真的想哭。以前遇到这种情况,基本只有两条路:要么花大几…

作者头像 李华
网站建设 2026/9/9 20:37:51

AI Agent记忆系统:从跨会话连续性到工程化落地

1. 为什么“让 Agent 记住你”不是功能升级,而是范式切换? “走进AI Agent第三篇:让 Agent 记住你”——这个标题乍看像一个普通功能点,但实际踩中了当前Agent落地最深的断层带。我从2022年第一批用LangChain搭客服Bot开始&#x…

作者头像 李华
网站建设 2026/9/9 20:36:05

物联网安全真相:从三层架构到攻击链的全面防护指南

什么是物联网?它真的安全吗?你有没有想过,家里的智能灯泡、楼下的快递柜、工厂里的机械臂、田里的灌溉传感器,其实都在同一张“网”里干活?这张网就是物联网(IoT,Internet of Things&#xff09…

作者头像 李华
网站建设 2026/9/9 20:34:52

2026年3月项目申报黄金月:110个项目筛选、匹配与材料准备全攻略

每年到了2月底3月初,做项目申报的朋友基本都进入“连轴转”状态。2026年3月的申报窗口尤其特殊,我粗算了一下,这个月释放出来的国家和地方可申报项目超过110个,资助强度从十万级到亿级都有,其中头部项目的单项支持上限…

作者头像 李华