4. MQTT协议基础与EMQX接入验证
EMQX 是面向物联网场景的高并发 MQTT 消息服务器,部署完成后,第一件必须确认的事不是“端口是否监听”,而是“客户端能否按 MQTT 语义正确接入、发布、订阅并收到消息”。本节围绕 MQTT 的发布/订阅模型、QoS 等级、保留消息与遗嘱消息四个核心机制展开,并使用 mosquitto 客户端对 EMQX 接入进行验证。
一、发布/订阅模型:主题是唯一的路由依据
MQTT 采用发布/订阅(Pub/Sub)模型,与 HTTP 的请求/响应模型有本质区别。发布者(Publisher)不直接把消息发给某个订阅者,而是把消息发布到某个主题(Topic);订阅者(Subscriber)向 Broker 声明自己关心哪些主题;Broker(此处即 EMQX)负责把消息按主题匹配规则转发给所有匹配的订阅者。发布者与订阅者彼此不知道对方的存在,二者通过主题解耦。
主题是一个用斜杠 / 分隔的层级字符串,例如 factory/line1/temp。主题区分大小写,且不需要预先创建。订阅时可以使用两种通配符:+ 匹配单层,# 匹配多层且只能出现在末尾。例如 factory/+/temp 可匹配 factory/line1/temp 与 factory/line2/temp,而 factory/# 可匹配 factory 下所有层级。需要注意,以 $ 开头的主题(如 $SYS/)不会被 # 匹配,EMQX 的系统主题就属于这一类。
下面这张图把发布/订阅模型、QoS 等级、保留消息与遗嘱消息放在同一条链路上,便于对照理解各机制发生的位置。
二、QoS 等级:可靠性、开销与重复的权衡
QoS 决定消息投递的可靠性等级,共三档。QoS 0 为“至多一次”,发布者发出后不等待确认,网络抖动或 Broker 重启都可能丢消息,适合高频、可容忍丢失的传感器数据。QoS 1 为“至少一次”,Broker 收到后回 PUBACK,若确认丢失会重发,因此可能重复,适合大多数遥测与指令场景。QoS 2 为“恰好一次”,通过 PUBLISH/PUBREC/PUBREL/PUBCOMP 四次握手保证不丢不重,但开销最大、时延最高,一般用于计费、开关动作等不允许重复的场合。
一个容易踩坑的点是:QoS 是发布与订阅两侧各自声明的,最终生效等级取两者中的较小值。若发布用 QoS 2、订阅用 QoS 0,实际投递仍是 QoS 0。因此排查“为什么消息不可靠”时,必须同时检查两侧参数。
三、保留消息与遗嘱消息
保留消息解决“订阅者上线太晚、错过上一次状态”的问题。发布时加上 retain 标志,EMQX 会为该主题保存最后一条保留消息;之后任何新订阅者一旦订阅匹配主题,会立刻收到这条消息。例如设备每分钟上报一次温度,若使用保留消息,监控端刚启动就能立即拿到当前温度,而不是干等一分钟。清除保留消息的方法是向同一主题发布一条空载荷的保留消息。
遗嘱消息解决“设备掉线如何被感知”的问题。客户端在 CONNECT 报文中预先声明遗嘱主题、载荷、QoS 与 retain 标志;当该客户端异常断开(网络中断、进程被杀、心跳超时)时,EMQX 会代为发布这条遗嘱消息。若客户端主动发送 DISCONNECT 正常断开,遗嘱不会触发。典型用法是设备上线时发布 online 保留消息,同时把遗嘱设为 offline,这样监控端既能知道设备在线,也能在设备掉线时立刻收到通知。
四、用 mosquitto 客户端验证 EMQX 接入
验证前先确认 EMQX 已启动且 1883 端口可访问。以下命令中的 IP、用户名密码均为示例,请替换为实际环境值。
# 1. 查看 EMQX 运行状态(示例)
sudo systemctl status emqx
# 2. 订阅端:订阅通配主题,-v 显示主题名,-q 指定订阅 QoS
mosquitto_sub -h 192.168.1.10 -p 1883 -t 'factory/+/temp' -q 1 -v
# 3. 发布端:发布一条 QoS 1 消息
mosquitto_pub -h 192.168.1.10 -p 1883 -t 'factory/line1/temp' -q 1 -m '26.5'
# 4. 保留消息验证:发布带 -r 的保留消息
mosquitto_pub -h 192.168.1.10 -p 1883 -t 'factory/line1/status' -q 1 -r -m 'online'
# 5. 新开一个订阅端,应立刻收到上一条保留消息
mosquitto_sub -h 192.168.1.10 -p 1883 -t 'factory/line1/status' -q 1 -v
# 6. 清除保留消息:发布空载荷
mosquitto_pub -h 192.168.1.10 -p 1883 -t 'factory/line1/status' -q 1 -r -n
# 7. 遗嘱消息验证:订阅告警主题后,用带遗嘱的客户端连接并强制中断
mosquitto_sub -h 192.168.1.10 -p 1883 -t 'factory/line1/status' -q 1 -v
mosquitto_pub -h 192.168.1.10 -p 1883 -t 'factory/line1/status' -q 1 \
--will-topic 'factory/line1/status' --will-payload 'offline' --will-qos 1 --will-retain \
-m 'online' -r
# 在另一终端用 Ctrl+C 强制结束上面的 pub 进程,观察订阅端是否收到 offline
若需要用户名密码接入,可在命令中追加 -u 用户名 -P 密码;若启用了 TLS,则把端口改为 8883 并追加 --cafile ca.crt。EMQX 默认允许匿名接入,生产环境应在 Dashboard 的“访问控制—认证”中关闭匿名并配置认证方式。
五、结果判读与常见问题
验证是否成功,可从三个层面判断。第一,连接层:mosquitto_sub 启动后无报错、不退出,说明 TCP 与 MQTT CONNECT 成功;若提示 Connection Refused: not authorised,多为认证或 ACL 拒绝。第二,路由层:发布后订阅端立即打印出 factory/line1/temp 26.5,说明主题匹配与转发正常;若收不到,优先检查主题拼写、通配符层级以及是否误用了 $SYS 类主题。第三,语义层:保留消息应在订阅瞬间到达,遗嘱消息应在客户端异常断开后到达,若遗嘱未触发,需确认客户端是异常断开而非主动 DISCONNECT。
| 验证项 | 关键参数 | 预期现象 | 异常时优先排查 |
|---|---|---|---|
| 基础接入 | -h / -p | 订阅端保持连接不退出 | 端口、防火墙、EMQX 进程 |
| 发布订阅 | -t / -q / -v | 订阅端打印主题与载荷 | 主题拼写、通配符、QoS 取值 |
| 保留消息 | -r | 新订阅者立即收到最后一条 | 是否带 -r、是否被空载荷清除 |
| 遗嘱消息 | --will-* | 异常断开后收到 offline | 是否主动 DISCONNECT、will 参数 |