为什么是它
设备上报的程序最容易卡住的地方,不是代码写不出来,是"数据到底走到哪一步了"。
典型场景:现场设备接好了,采集程序也在跑,但云平台上什么都看不到。这根链路中间有三方——设备、Broker(消息服务器)、订阅端,而且谁都不报错。你只能猜:设备根本没发?发了但格式不对?Broker 拒收了?还是订阅端的主题写错了?
排这种问题,需要一个能以第三方身份独立接入的工具:它不掺和你的业务代码,自己连上 Broker、自己订阅,看看到底有没有消息过来、内容长什么样。
MQTTX 就是干这个的。它是 EMQX(MQTT Broker 领域最主流的厂商之一)开源的 MQTT 5.0 客户端,Apache-2.0 协议,一套内核三种形态:
活跃度用硬指标说话(这些都是查得到的,不是形容词):
同类工具横向对比,方便你判断该用哪个:
先用五分钟把 MQTT 搞懂
MQTTX 的界面很直观,但如果不知道协议在做什么,你看到的只是一堆字符串。下面这五件事搞明白,后面所有操作都能对上号。
1. 发布/订阅,不是请求/响应
和 HTTP 最大的区别:发消息的人不认识收消息的人。
┌──────────────┐
设备 ──publish──▶│ │──deliver──▶ 订阅端 A
(只负责发) │ Broker │
│ (消息服务器) │──deliver──▶ 订阅端 B
采集程序 ──────▶ │ │
└──────────────┘
按"主题"路由,不按地址
三方互不直连,全靠一个中间人(Broker)按主题(Topic)转发。好处是解耦:设备只管往主题上发,谁需要谁自己订阅,加一个订阅方不用改设备一行代码。
2. 主题(Topic):一条用 / 分层的字符串
factory/line1/plc01/temperature
│ │ │ │
厂区 产线 设备 数据
订阅时可以用两个通配符:
两条硬规则,写错了订阅不会报错,只会静默收不到消息:
#只能出现在最后一级,factory/#/temperature是非法主题;+必须独占一层,factory/line+/temperature也非法。
另外,以 $ 开头的主题(如 $SYS/)不会被 # 或 + 开头的订阅匹配到,必须显式订阅 $SYS/#。这是刻意的设计,防止通配符把 Broker 自身的运行指标一并吞掉。
3. QoS:三种投递保证
工程上的常见选择:周期性的传感器数据用 0 或 1(丢一条下一周期就补上了),开关指令、计费事件用 1 或 2。
4. Retain(保留消息):给每个主题存一份"最新值"
普通消息只在发布的那一刻转发给当时的在线订阅者。加了 Retain 标志后,Broker 会把这条消息存在这个主题上,之后任何新订阅者一连上来就能立刻收到。
它的典型用途是"当前状态"——新上线的监控界面一订阅,马上就能看到设备的最新值,不用傻等下一个上报周期。
一个必须记住的限制:Retain 只保留每个主题的最后一条。它不是历史数据库,想存历史得靠订阅端自己落盘。
5. Will(遗嘱消息):设备"死了"也要说一声
连接时可以预先注册一条遗嘱消息。如果这个客户端异常断开(断网、断电、进程被杀),Broker 会替它把这条消息发出去。
配合 factory/line1/plc01/status 这类主题,就能实现"设备离线自动告警",不需要设备端写任何额外逻辑。
端口和版本
MQTT 3.1.1 和 5.0 的差别,对你日常调试影响最大的是 5.0 新增的能力:原因码(连不上时告诉你为什么,而不是默默断线)、用户属性(在消息头里塞自定义元数据)、会话过期间隔(断线后会话保多久)、共享订阅(多个订阅方负载均衡)、主题别名(省带宽)、消息过期间隔(过期数据自动丢弃)。
MQTTX 默认按 5.0 连接,对不支持的 Broker 用 -V 3.1.1 切回去。
安装
桌面版
命令行 CLI
官方主推独立二进制(就是官网说的 "dependency-free")——从 GitHub Releases 直接下,不用装 Node:
如果你本来就有 Node 环境,用 npm 也行:
npm install -g mqttx-cli@latest
mqttx --version
注意写
@latest。 实测遇到过npm install -g mqttx-cli装出来是 1.10.1(2024 年的版本),而当时最新已经是 1.13.1——大概率是 registry 镜像或本地缓存的旧元数据导致的。显式写@latest就不会踩到。不想折腾就直接下独立二进制。
Docker 里跑(适合服务器上短暂排查):
docker run -it --rm emqx/mqttx-cli pub -t test -h broker.emqx.io -m "hello"
网页版
浏览器打开 mqttx.app,不用安装。适合临时借用别人电脑排查——但连接信息和账号密码是打到对方浏览器里的,敏感环境别用。
一个免费的公共 Broker
不想自己搭 Broker 也能练手,EMQX 提供了公开测试服务器:
Broker: broker.emqx.io
TCP 端口: 1883
SSL 端口: 8883
⚠️ 公开服务器上任何人的消息互相可见,只放测试用的假数据。
桌面版:五步上手
界面逻辑和聊天软件很像——左边是"会话列表",中间是"消息流"。
① 新建连接:点左侧栏的 +,填连接表单。
② 连接:点右上角 Connect。状态变成已连接就成功了。
③ 订阅:在订阅区点 New Subscription,填主题(如 factory/#),选 QoS,可以给不同主题配不同颜色方便区分。
④ 发布:在右上发布区填主题、QoS,勾选 Retain 就是保留消息,粘贴载荷后点发送。
⑤ 看消息:中间区域实时滚出收到的消息,每条都能展开看完整内容。
桌面版还有几处值得知道的:
载荷格式:支 Plaintext / JSON / Base64 / Hex 互转。收到一坨乱码时切 Hex 看,往往一眼就认出结构;
多窗口 / 多连接:同时连多个 Broker 或同 Broker 上多个客户端,互不干扰;
脚本:可以对消息跑一段 JavaScript 做加工(v1.4.2 起支持);
流量统计:订阅
$SYS/#能看到 Broker 的连接数、消息速率等运行指标;TLS 完整支持:CA 证书、自签名证书、双向认证都能配。
CLI 速查表
CLI 是这篇的重点——桌面版会点鼠标就行,CLI 才是能写进脚本、能上服务器的那一半。
连接 / 发布 / 订阅
常用参数
消息体的四种来源
遗嘱消息(设备离线上报)
压测与模拟
几个参数要留意:
⚠️ bench 的默认值是 1000 个连接。mqttx bench pub 后面什么都不加就回车,会朝对端压一千条连接——千万别对着生产 Broker 裸跑,先显式写 -c 小数值试水。
五个真实场景
场景 1:三十秒判断"设备到底发了没有"
这是最常用的。开两个终端窗口:
# 窗口 A:订阅整个前缀下的所有消息
mqttx sub -t 'factory/#' -h 192.168.1.200 -p 1883 -q 1
# 窗口 B:(设备已经在发的前提下)看看有没有东西过来
如果 A 里什么都没有,问题在设备端或链路上游;如果有消息但内容不对,问题在载荷格式;如果只有部分主题,问题在主题命名。一步就把范围缩小了。
场景 2:用管道把采集结果直接发上去
-s 让消息体从标准输入进来,于是任何能往标准输出打印的命令,都能变成一条 MQTT 消息:
# 手工发一条 JSON
echo '{"deviceId":1,"temp":25.6,"hum":60}' \
| mqttx pub -t 'factory/line1/plc01/data' -h 192.168.1.200 -q 1 -s
在采集程序(不管是 Python、C# 还是 shell 脚本)里,也可以不做 MQTT 库的集成,先把结果打成 JSON 丢到标准输出,由 mqttx 负责发送。快速验证链路的阶段,这比引入一个客户端 SDK 快得多。
实测的发布端输出:
- Connecting...
√ Connected
- Message publishing...
√ Message published
场景 3:订阅端看消息,并做格式化
订阅端不加 -f 是原样打印,加了 -f json 会美化成缩进结构(前提是载荷确实是 JSON):
mqttx sub -t 'factory/line1/#' -h 192.168.1.200 -q 1 -f json
实测输出:
received_at: 2026-09-20T12:57:12.203Z, topic: factory/line1/plc01/data, qos: 1, size: 27B
{
"deviceId": 1,
"temp": 25.6,
"hum": 60
}
如果收到的是二进制报文(比如设备直接推的原始寄存器数据),换 -f hex 看:
mqttx sub -t 'device/raw/#' -h 192.168.1.200 -f hex
实测输出(发的是 7 字节 01 03 04 41 CC CC CD):
received_at: 2026-09-20T12:57:36.343Z, topic: device/raw/1, qos: 1, size: 7B
0103 0441 cccc cd
Hex 分组显示,41 CC CC CD 这种一眼就能看出是 IEEE754 浮点数 25.6。
但这里有个坑:-f json 是强制按 JSON 解析的,收到非 JSON 载荷时会直接刷错误:
× SyntaxError: Unexpected token '', "A??" is not valid JSON
所以别在既收 JSON 又收二进制的主题上用 -f json,老老实实看原始输出、需要时单独订阅。
场景 4:自动记录 + 落盘
订阅端可以边收边写文件,长时间挂机观察设备行为时很有用:
# 追加写入一个文件(适合累积日志)
mqttx sub -t 'factory/#' -h 192.168.1.200 --file-write factory.log
# 每条消息存成一个独立文件(适合分析单条报文)
mqttx sub -t 'factory/#' -h 192.168.1.200 --file-save ./dump
# 换分隔符,方便后续用脚本按行切分
mqttx sub -t 'factory/#' -h 192.168.1.200 --file-write a.log --delimiter ','
场景 5:上线前压一下
联调通了、要上线之前,值得做两件事:
# ① 测 Broker 能扛多少连接
mqttx bench conn -c 500 -i 10
# ② 测上行吞吐:20 个客户端,每个每秒 5 条(间隔 200ms),共 1000 条
mqttx bench pub -c 20 -im 200 -L 1000 -t 'bench/%i' \
-h 192.168.1.200 -q 1 -m 'ping'
实测输出:
❯ Starting publish benchmark, connections: 2, req interval: 10ms, message interval: 200ms
√ Created 2 connections in 1.441s
Published total: 5, message rate: 5/s
√ All 5 messages have been sent, reaching the limit of 5.
%i 会被替换成客户端序号,于是每个连接往不同主题发,不会互相覆盖。配合 --payload-size 1KB 还能模拟真实载荷大小。
主题命名规范:现在花十分钟,后面省很多事
主题一旦上线就很难改(改了所有订阅方都得跟着改),建议一开始就定好:
一个补充技巧:共享订阅。 如果你的数据要在多个处理服务之间做负载均衡(同一条消息只需要被其中一个处理),EMQX 支持共享订阅写法:
$share/{组名}/{主题} 例如订阅:$share/workers/factory/line1/#
同一个组内多个订阅者,消息只会投给其中一个。这个能力由 Broker 提供(EMQX 支持,部分开源 Broker 也支持),不是 MQTT 协议本身的语法,用之前确认一下你的 Broker 版本。
容易踩的坑
上手路径
第一阶段:先能用(半小时)
装桌面版,连
broker.emqx.io:1883;订阅
#,看看公共服务器上都有什么消息在飞;自己发一条,确认能收到;
记住三件事:主题怎么分层、QoS 在哪选、载荷格式在哪切。
第二阶段:接到自己的环境(一小时内)
装 CLI(独立二进制最省事);
用
mqttx conn确认能连上你的 Broker;mqttx sub -t '#'看你的环境里到底有哪些主题在发——这一步经常直接发现问题;用
mqttx pub -s配管道发一条,验证上行。
第三阶段:进工具链
把
mqttx conn的返回码接进 CI 或监控脚本,做成连通性检查;给关键设备配上遗嘱消息,实现离线告警;
上线前用
bench跑一轮,心里有个数。
第四阶段:按需深入
载荷是二进制的,用
-f hex/-f base64摸清结构;配 TLS 和认证;
需要模拟成百上千台设备时,用
simulate内置场景。
参考
项目主页:https://mqttx.app
桌面版与 CLI 下载:https://mqttx.app/downloads
CLI 文档:https://mqttx.app/docs/cli
桌面版手册:https://mqttx.app/docs
源码仓库:https://github.com/emqx/MQTTX
公共测试 Broker:
broker.emqx.io(TCP 1883 / SSL 8883)