1,MQTT 是什么
MQTT(Message Queuing Telemetry Transport) 是一种轻量级的发布/订阅(Pub/Sub)消息协议,!!它是协议,不是平台,就像 HTTP、TCP 一样,任何人都可以用它来自定义通信。最早由 IBM 提出,用于低功耗、低带宽场景下的数据传输,尤其适用于物联网(IoT)设备通信。
名称 | 全称 | 角色 | 类比 |
---|---|---|---|
MQTT | Message Queuing Telemetry Transport | 一种协议(通信规则) | 寄快递的规则,寄件人、寄给谁、格式等规定 |
MQTT实现者 | 如:EMQX、Mosquitto、HiveMQ等 | MQTT协议的具体产品 | 快递公司,比如顺丰、圆通、韵达等 |
类比参考:MQTT 与其实现者(如EMQX)的关系,就像 AMQP 与 RabbitMQ 的关系。
MQTT 核心概念如下:
角色 | 说明 |
---|---|
Broker(消息中转中心) | MQTT服务器,负责转发消息 |
Publisher(发布者) | 比如无人机,定时上报状态(如定位、电量) |
Subscriber(订阅者) | 比如后端系统,订阅无人机状态,接收消息 |
Topic(主题) | 消息分类路径,类似URL路径,如:drone/123/status |
举个例子:温度传感器
-
每一个设备是一个 MQTT 发布者(Producer),它定时上报温度数据;
-
手机、电脑、服务器、Java等等,都可以是MQTT 的订阅者(Consumer)也就是客户端,订阅特定 topic,接收发来的信息;
-
中间的通信由一个 MQTT 实现者(如 EMQX)负责中转和管理。
比如,图中 展示了 MQTT 的发布/订阅过程。温度传感器作为客户端连接到 MQTT Broker,并通过发布操作将温度数据发布到一个特定主题(例如 Temperature)。MQTT Broker 接收到该消息后会负责将其转发给订阅了相应主题(Temperature)的订阅者客户端。
(图片出自EMQX,侵删)
2,MQTT 的优势和应用场景
特性 | 说明 |
---|---|
低带宽、低功耗 | 消息很小,适合物联网设备(比如蓝牙门锁、智能电表) |
发布/订阅模型 | 不需要设备互相知道 IP |
支持 QoS 等级 | 保证消息“至少一次”“至多一次”甚至“正好一次” |
支持保留/遗嘱消息 | 可以通知别人“我下线了”或者“我挂了” |
支持断线重连 | 网络差也能恢复连接 |
应用场景
应用场景 | 说明 |
---|---|
智能家居 | 比如智能插座、温湿度传感器 |
汽车物联网 | 车辆数据上传,远程监控 |
工业自动化 | 远程设备状态监控 |
智能门锁 | 控制开关门、上传日志等 |
3,MQTT 与 MQ 的关系与异同
MQTT和MQ是两个非常容易被混淆的概念。很多开发者容易把它和 RabbitMQ、Kafka 混为一谈。其实二者在定位、层次、使用场景上都存在显著差异。
MQTT 常用于 IoT 场景,而 MQ 通常是指实现了各种消息传输协议的中间件产品,如 RabbitMQ、RocketMQ 等。两者都基于生产者-消费者-Broker 架构,但 MQTT 是协议层,MQ 是产品层。
区别对比表
对比维度 | MQTT | MQ |
---|---|---|
本质 | 协议 | 架构/中间件产品 |
设计目标 | 轻量、低带宽、低功耗、实时 | 解耦服务、异步处理、高吞吐 |
模式 | 发布/订阅(Pub/Sub) | 多种:点对点、发布订阅、事务型等 |
使用者 | 通常用于 物联网设备 通信 | 通常用于 后端服务之间通信 |
常见产品 | EMQX、Mosquitto、HiveMQ | RabbitMQ、Kafka、RocketMQ、ActiveMQ |
消息确认 | 有 QoS(0, 1, 2)级别控制 | 更强的持久化、事务等保障 |
协议层 | 应用层协议(TCP/IP之上) | 实现通信机制的一种手段(不局限于协议) |
适合场景 | 智能家居、远程遥测 | 电商下单、日志收集、订单系统异步解耦 |
比如现在有个智能门锁,它会在我开门的时候,记录下是指纹开门、人脸开门等。就是通过MQTT传递到后端,然后后端将这些动作记录到数据库,此时呢可以采用MQ进行异步处理。
他们最核心的差异:
- 消费模型不同(最本质差异)
MQ默认是竞争式的(就是一个消费者消费了,其它人就不用消费了。当然在一些MQ产品中比如RabbitMQ等,你也可以设成广播的)。
而MQTT 是“广播式”消费模型。它会通知这个topic上的所有订阅者。
实际上准确来说MQTT是"多播",也就是逐个分发给订阅者,并不是真正意义上的广播协议,它使用主题(Topic)做路由匹配,实现“组播效果”,但每个客户端的连接、QoS、ACK 都是单独维护的,因此它是“看起来像广播,实际上是点对点多路投递”。
- 订阅行为不同
MQTT 的订阅更灵活、轻量,无需提前声明结构。
- MQTT 中,客户端只需订阅指定的 Topic 路径(如:device/lock/open) 即可,无需显式创建 Broker、Exchange、Queue。
- MQ 则需要开发者显式声明:交换机、队列、绑定关系、路由规则,配置更复杂,但功能更强大。
- 消息存储机制不同
MQTT 默认不持久化消息,而 MQ 的消息则通常会被可靠地存储。
- MQTT 的 Broker(如 EMQX)默认仅在 QoS=1/2 且 cleanSession=false 时才考虑持久化消息,每个 Topic 最多只会保留一条 Retain 消息(即“最后一条”),不会保存历史记录。
- MQ(如 Kafka)则默认将消息持久化到磁盘,即便没有消费者在线,消息也不会丢失,支持延迟处理、离线分析等复杂场景。
MQTT 可以根据QoS的级别来配置"生产"消息这个行为一定被"生产"成功,但不保证有客户端在监听,MQTT Broker像一个喇叭,根据配置的QoS级别,它保证自己一定会"喊一下"这个喇叭。但不保证它喊的喇叭会被人听见,又或者有几个人听见。而MQ在这方面则完全不同,它在收到消息后是保证本地先存起来,防止消息丢失,再去处理后续的消息消费。
4,为什么物联网领域强烈推荐 MQTT 而不用HTTP
你必须要理解下面这几点。
在可能不稳定网络环境(家庭 Wi-Fi 信号边缘、蜂窝网络) 的物联网设备,MQTT 的设计优势是碾压性的。“专为物联网和资源受限环境设计”,意味着 MQTT 在 网络不好、设备性能差、电量有限、通信频繁但数据量小的环境下,表现比 HTTP 更加稳定、节能、实时、可靠。
场景:一个低功耗蓝牙门锁,开锁上传一条记录。
维度 | HTTP | MQTT | 说明 |
---|---|---|---|
连接方式 | 每次都要重新建立连接(TCP + TLS + 传头部) | 长连接(TCP 只建立一次) | MQTT 建立连接后,可以一直保持(心跳维持) |
数据体积 | 较大(有大量 header) | 极小(只有必要数据) | MQTT header 只占 2 字节起 |
实时性 | 发了就断(非实时) | 实时推送,毫秒级 | 适合做“开锁指令下发” |
功耗 | 连接 + 断开消耗高 | 一直连接,省电 | 特别重要!门锁是电池供电 |
网络要求 | 网络波动时易失败 | 支持断线重连、QoS | MQTT 可以断线缓存、自动重发 |
传输方向 | 客户端只能请求服务器 | 服务端也可以主动下发指令 | HTTP 无法推送(除 WebSocket) |
协议层 | 应用层 HTTP | 应用层 MQTT(基于 TCP) | MQTT 比 HTTP 更轻 |
HTTP 像是打一次电话就挂断,MQTT 像是一直保持在线的对讲机,适合设备随时上下线、随时传消息的环境。
作为IoT设备通信间的最佳协议之一,MQTT还提供了非常多实用的机制和功能。
5,掉线感知
MQTT 可以感知设备掉线,而且非常精准、轻量!有点类似于WebSocket 的连接断开而且比 WebSocket 更适合资源受限的设备场景,主要依赖两部分:心跳机制 + 遗嘱消息机制。
5.1 心跳机制(Keep Alive)
工作原理:
-
客户端连接时会告诉 Broker 一个 Keep Alive 时间间隔(比如 60 秒)
-
这个间隔内,客户端必须发送任意一条 MQTT 报文(哪怕是 PINGREQ 心跳包);
-
如果 Broker 在 1.5 倍 Keep Alive 时间 内 没收到客户端任何消息,就认定这个客户端“掉线”;
-
Broker 会主动断开连接。
心跳包说明:
-
MQTT 提供专门的心跳包叫 PINGREQ / PINGRESP
-
一般是客户端发送 PINGREQ,Broker 回复 PINGRESP
5.2 遗嘱消息(Last Will and Testament)
工作原理:
-
客户端在连接时就设置好遗嘱消息(指定 topic、内容、QoS);
-
如果客户端正常断开连接(调用 DISCONNECT),不会触发遗嘱;
-
如果客户端意外掉线(如断电、断网,没发 DISCONNECT,心跳失联),
-
Broker 会在感知到客户端掉线后,自动向预设的 topic 发布这个遗嘱消息;
-
订阅了该 topic 的其他客户端就能收到通知,比如 “设备123离线了”。
-
5.3 模拟一下连接流程:
-
设备上线,连接 MQTT Broker 时可以设置一个“遗嘱消息”(Will Message):
如果我哪天突然没了,请你替我 —— 发个 offline 消息到 lock/status/LOCK123456 这个 Topic。
-
Broker 记住这份遗嘱了,设备继续正常通信
如果设备断电、断网、挂了
-
Broker 开始等……等你发心跳(KeepAlive 时间 + 容忍时间)
-
超时没收到,Broker 确认:设备“失联了”
-
此时 Broker 代表设备,替你发出遗嘱消息(offline)!
6,送达确认机制(QoS 质量等级)
在实际业务中,我们经常需要确保“指令是否真正送达”。MQTT 协议为此设计了消息质量等级(QoS),它控制了消息在传输过程中的可靠性和重复性保障程度。
6.1 MQTT 支持的三种 QoS 等级如下:
QoS 等级 | 名称 | 含义 | 是否可能重复 |
---|---|---|---|
0 | 至多一次 | 发了就不管了,不确认是否送达 | ❗可能丢失 |
1 | 至少一次 | 确保 Broker 收到,可能重复投递 | ✅可能重复 |
2 | 仅一次 | 完整的四次握手确保只投一次 | ✅不会重复但性能最低 |
MQTT 的消息从“生产者发出”到“消费者收到”分为两段:
[生产者 → Broker] → [Broker → 订阅者]
生产者和订阅者都可以设置自己的 QoS,这两个方向相互独立控制,共同决定了消息最终送达订阅者的实际保障等级。
6.2 生产者 QoS:控制“是否送达 Broker”
- 生产者通过 client.publish(topic, qos) 设置发送的 QoS 等级;
- 控制的是:消息是否被 Broker 接收并确认(ACK);
- Broker 必须根据生产者的设置走对应的握手流程(如 QoS 1 的 PUBACK、QoS 2 的四步流程);
- 与订阅者是否存在、怎么订阅无关。
- !!注意:QoS即使设置为1/2,也不代表它一定被人消费。它只能保证自己的消息到Broker了,有没有消费者、消费是否成功 则是一律不管的。
6.3 订阅者 QoS:控制“Broker 如何推送消息给我”
- 订阅者通过 client.subscribe(topic, qos) 声明自己“最多能接受什么等级的消息”;
- QoS 越高,意味着客户端需要处理更多的存储、去重、包序号等逻辑;
- Broker 不得超过订阅者请求的 QoS 等级进行推送.
订阅者 QoS = 1,意味着它希望 Broker 发来的消息最多是 QoS 1,即使原始消息是 QoS 2,也必须降级。也就是说 订阅者的QoS其实是一种预期,表示 “我可以接收最高什么级别的”。而当原始消息低于我们的预期时,将降级成原始消息的级别。反过来,我们预期低于原始消息,也会降级成我们预期的级别。
6.4 最终投递 QoS(Deliver QoS)
图示:
┌─────────────┐
│ 发布者 │
│ QoS = 2 │
└────┬────────┘
│
▼
┌─────────────┐
│ Broker │
└────┬────────┘
│
┌────────────────┬──────────────┐
▼ ▼ ▼
订阅者:A (QoS2) B(QoS1) C(QoS0)
最终QoS:
A = min(2,2) = 2
B = min(2,1) = 1
C = min(2,0) = 0
反过来:
┌─────────────┐
│ 发布者 │
│ QoS = 0 │
└────┬────────┘
│
▼
┌─────────────┐
│ Broker │
└────┬────────┘
│
┌───────────────┬──────────────┐
▼ ▼ ▼
订阅者A (QoS2) B(QoS1) C(QoS0)
最终QoS:
A = min(0,2) = 0
B = min(0,1) = 0
C = min(0,0) = 0
6.5 一个常见误区:是不是都设置 QoS = 2 就最安全?
你可能会想:干脆订阅者都设 QoS=2,是不是最保险?
技术上可以这么做,但并不推荐在生产中“统一拉满”,原因如下:
问题点 | 说明 |
---|---|
心理误区 | 设置 QoS=2 会误导团队以为系统已足够可靠,反而忽略幂等、重试、日志等业务层保障 |
客户端资源消耗 | 客户端 SDK 会预初始化 QoS 2 相关的结构,如包序号缓存、持久化机制,占用更多资源 |
性能下降 | QoS 2 要走四次握手,导致消息流转速度大幅降低,特别在弱网环境或 IoT 场景下更明显 |
这里有个问题:QoS2,需要四次握手,而HTTP还只需要3次。它是不是比HTTP还重,有没有必要用?
6.6 MQTT QoS2 的四次握手详细流程
MQTT 的 QoS2 确实比 HTTP 的三次握手“还重”,但它解决的是“消息投递这件事本身的幂等性”,而 HTTP 的三次握手解决的是“TCP 连接可靠性”,两者完全不一样。
举个例子:
设备发一条消息:"门锁已打开"
步骤如下:
客户端 → Broker
PUBLISH(消息包,带 QoS2 标志)Broker → 客户端
PUBREC(我收到了,准备确认)客户端 → Broker
PUBREL(你确认我发的吧)Broker → 客户端
PUBCOMP(确认完成了,别再发了)
这就是所谓的 PUBLISH → PUBREC → PUBREL → PUBCOMP 四步。
6.7 常见场景下该选哪种 QoS?
场景 | 推荐 QoS 等级 | 是否能接受消息重复? | 是否能接受消息丢失? | 说明 |
---|---|---|---|---|
🔓 设备上报“门已打开” | QoS 1(至少一次) | ✅ 能接受(服务去重) | ❌ 不接受(不能丢记录) | 丢就没记录,重复最多多一条 |
📡 设备定时上报温度、电量等状态 | QoS 0(至多一次) | ✅ 能接受 | ✅ 能接受 | 实时性优先,偶尔丢一条无影响 |
📦 远程开锁指令(云端 → 设备) | QoS 2(仅一次) | ❌ 不能接受 | ❌ 不能接受 | 重复=二次开锁,严重问题 |
📲 设备上线通知(状态变化) | QoS 1 | ✅ 能接受 | ❌ 不接受 | 若状态丢失,会认为设备掉线 |
🛠️ 配置/升级命令 | QoS 2 | ❌ 不能接受 | ❌ 不能接受 | 丢了没配置,重复执行出错 |
🧪 数据采样(每秒10条) | QoS 0 | ✅ 能接受 | ✅ 能接受 | 高频无状态上报,重发浪费带宽 |
👨 用户操作反馈(按钮点击) | QoS 1 | ✅ 能接受 | ❌ 不接受 | 丢失可能影响用户体验 |
⚠️ 告警信息上传 | QoS 1 或 2(看严重级别) | ❌ 不接受 | ❌ 不接受 | 丢失报警 = 灾难 |
6.8 总结:
MQTT 的 QoS2 就是为了解决“消息只能送达一次”这个高要求场景而设计的,它通过四次握手实现消息发出的幂等性,性能较低但可靠性最高。但绝大多数场景下,建议使用 QoS 1即可。因为即使是QoS2,在业务中依旧要做幂等处理(因为往往我们的消费者不会只有一个)。
7,断网期间消息缓存(Session + 离线队列)
为了应对“设备断网”这种常见场景,MQTT 还提供了“持久会话”和“离线消息缓存+补发”机制,只要你用对了配置,这些能力就自动为你工作,不需要手动管太多逻辑。
场景一:设备是发布者
比如:设备 ➜ 上报开锁事件 lock/event/open
// 1. 保持会话
options.setCleanSession(false);
// 2. 设置消息QoS
message.setQos(1);
这样配置的作用是:
如果设备临时断网(比如掉线3秒),MQTT SDK 会自动把这段时间要发的消息缓存起来,等网络恢复后补发出去,你无需自己管理“重发队列”。
场景二:设备是订阅者(用于接收服务端下发命令)
比如:平台 ➜ 给设备下发 “关锁指令” 到 lock/cmd/close
设备代码这样写:
options.setCleanSession(false); // 保持会话
client.subscribe("lock/cmd/close", 1); // 订阅指令主题,QoS=1
这时 cleanSession=false 的意义是:
如果设备断网,MQTT Broker 会保存这段时间发给它的消息(只要 QoS > 0),等设备一上线就自动推给它。
再对比 HTTP :
项目 | MQTT(带 QoS + 缓存) | HTTP |
---|---|---|
断网时消息能否保存? | ✅ 会缓存 | ❌ 失败直接丢 |
断网后能否补发? | ✅ 自动重发 | ❌ 你要自己记住重新发 |
是否确认送达? | ✅ 有 ACK 和 QoS | ❌ 没有(除非你设计重试) |
总结
MQTT 作为一种为物联网量身打造的轻量级通信协议,凭借其低带宽消耗、长连接机制、灵活的消息确认等级以及断线重连能力,已经成为智能设备通信的首选。相比传统的 HTTP 通信,它不仅更实时、省电,而且在弱网和高不确定环境中依然表现稳定。对于我们开发者而言,深入理解 MQTT 的核心机制(如 QoS、遗嘱消息、持久会话等)能够帮助我们构建出更可靠、更高效的 IoT 应用系统。未来随着物联网设备的普及,MQTT 的应用价值将愈发凸显,是每一位从业者必须掌握的重要技术。接下来我将会带来MQTT热门产品:EXMQ的部署和使用,敬请期待。