vault backup: 2026-06-08 23:08:57
This commit is contained in:
@@ -15,14 +15,34 @@ MQTT(Message Queuing Telemetry Transport)是专为物联网(IoT)场景
|
||||
|
||||
想象一个场景:成千上万个温度传感器部署在偏远地区,它们通过 2G/3G 网络上报数据。这些设备 CPU 弱、内存小、电量有限、网络时断时续。在这种约束下,HTTP 的冗长头部和请求-响应模型就成了灾难。MQTT 就是为解决这类问题而生的——它把协议开销压缩到极致(最小报文仅 2 字节),支持持久连接,提供灵活的消息可靠性保障。
|
||||
|
||||
### 版本演进
|
||||
|
||||
| 版本 | 年份 | 关键变化 |
|
||||
|------|------|----------|
|
||||
| MQTT 3.1 | 2010 | 协议于 1999 年由 IBM 设计,此版本首次公开发布,奠定发布-订阅、QoS、遗嘱消息等核心设计 |
|
||||
| MQTT 3.1.1 | 2014 | OASIS 标准化,清理协议细节(如 CONNACK 返回码规范化),成为最广泛部署的版本 |
|
||||
| MQTT 5.0 | 2019 | 企业级升级:共享订阅、用户属性、原因码、Topic Alias、增强认证等,补齐生产环境短板 |
|
||||
|
||||
### 发布-订阅架构
|
||||
|
||||
MQTT 采用以 Broker 为中心的发布-订阅模型,发布者和订阅者完全解耦:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
P1["Publisher 1"] -->|publish| B["Broker"]
|
||||
P2["Publisher 2"] -->|publish| B
|
||||
B -->|push| S1["Subscriber A"]
|
||||
B -->|push| S2["Subscriber B"]
|
||||
B -->|push| S3["Subscriber C"]
|
||||
```
|
||||
|
||||
### 报文结构
|
||||
|
||||
MQTT 的每条报文由三部分组成:
|
||||
|
||||
```
|
||||
+------------------+--------------------+----------+
|
||||
| Fixed Header | Variable Header | Payload |
|
||||
+------------------+--------------------+----------+
|
||||
```mermaid
|
||||
graph LR
|
||||
A["Fixed Header (必选)"] --> B["Variable Header (可选)"] --> C["Payload (可选)"]
|
||||
```
|
||||
|
||||
- **Fixed Header**(必选):包含报文类型(4 bit)、标志位(4 bit)和剩余长度(可变字节编码)。报文类型定义了 14 种操作,如 CONNECT、PUBLISH、SUBSCRIBE 等。
|
||||
@@ -32,6 +52,32 @@ MQTT 的每条报文由三部分组成:
|
||||
> [!question] 为什么 Fixed Header 的"剩余长度"要用可变字节编码(Variable Byte Encoding)?
|
||||
> 因为 IoT 场景下大部分消息体很小(几十字节),如果用固定 4 字节表示长度就太浪费了。可变编码让 0-127 字节的长度只占 1 字节,128-16383 字节占 2 字节,以此类推——小消息用小开销,大消息也能表示。
|
||||
|
||||
### 连接生命周期
|
||||
|
||||
一个 MQTT 客户端从连接到断开的完整流程如下:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "Client"
|
||||
participant B as "Broker"
|
||||
|
||||
C->>B: "CONNECT (ClientID, CleanSession, KeepAlive, Will)"
|
||||
B-->>C: "CONNACK (return code)"
|
||||
|
||||
Note over C,B: "正常通信阶段"
|
||||
C->>B: "PUBLISH / SUBSCRIBE / UNSUBSCRIBE"
|
||||
B-->>C: "PUBACK / SUBACK / UNSUBACK"
|
||||
|
||||
Note over C,B: "Keep Alive 心跳"
|
||||
C->>B: "PINGREQ"
|
||||
B-->>C: "PINGRESP"
|
||||
|
||||
C->>B: "DISCONNECT"
|
||||
Note over C,B: "正常断开, Broker 不发送遗嘱消息"
|
||||
```
|
||||
|
||||
**Keep Alive 心跳**:客户端在 CONNECT 中声明一个心跳间隔(秒)。如果在 1.5 倍间隔内没有任何报文发送,Broker 会认为客户端已断线并触发遗嘱消息。这个机制既保证了连接活性检测,又在通信频繁时避免了无意义的心跳包。
|
||||
|
||||
### QoS 等级:三种可靠性语义
|
||||
|
||||
MQTT 最精妙的设计之一就是三个 QoS(Quality of Service)等级,让发布者按需选择投递保证,而不是"一刀切"。
|
||||
@@ -78,6 +124,9 @@ sequenceDiagram
|
||||
> [!question] QoS 2 的四次握手会不会很慢?在什么场景下值得用?
|
||||
> 相比 QoS 0/1,QoS 2 确实多了额外的往返开销。但在需要精确计数的场景下(如扣费指令、库存变更、IoT 设备的命令下发),一条消息重复执行可能导致严重后果。实际部署中,很多系统选择 QoS 1 + 消费端幂等的组合来平衡性能和可靠性,只在真正"不能重复"的关键路径上才用 QoS 2。
|
||||
|
||||
> [!warning] QoS 降级规则
|
||||
> Broker 转发消息时,实际投递的 QoS 取 **发布者 QoS** 和**订阅者 QoS 的较小值**,即 `effective QoS = min(publisher QoS, subscriber QoS)`。例如发布者用 QoS 2 发送,但订阅者只订阅了 QoS 1,那 Broker 只保证 QoS 1 的语义。这是新手最容易忽略的细节。
|
||||
|
||||
### 核心概念
|
||||
|
||||
#### Topic 与通配符
|
||||
@@ -101,6 +150,9 @@ MQTT 使用层级结构的 Topic 进行消息路由,用 `/` 分隔层级,例
|
||||
- **Clean Session = 1**:每次连接都是全新会话,Broker 不保留任何订阅关系和未确认消息。适合无状态客户端。
|
||||
- **Clean Session = 0**:Broker 为客户端持久化会话(订阅列表 + QoS 1/2 的未确认消息)。客户端断线重连后能恢复之前的订阅并收到离线期间的消息。
|
||||
|
||||
> [!tip] MQTT 5.0 的变化
|
||||
> MQTT 5.0 将 Clean Session 拆分为两个独立概念:**Clean Start**(连接时是否创建新会话)和 **Session Expiry Interval**(会话保留时长)。这比 3.1.1 的非黑即白灵活得多——你可以"连接时用新会话,但断线后保留 1 小时"这种组合。
|
||||
|
||||
### MQTT 5.0 新特性
|
||||
|
||||
MQTT 5.0(2019 年发布)是一次重大升级,引入了多项企业级特性:
|
||||
@@ -110,6 +162,9 @@ MQTT 5.0(2019 年发布)是一次重大升级,引入了多项企业级特
|
||||
- **原因码(Reason Codes)**:所有响应报文都携带结构化的原因码,取代了之前模糊的返回码,方便客户端精确处理错误(如"Topic 名称不合法" vs "鉴权失败")。
|
||||
- **Session Expiry Interval**:可以设置会话过期时间,不再只有"立即过期"和"永不过期"两种选择。
|
||||
- **请求-响应模式**:引入 Response Topic 和 Correlation Data 属性,让 MQTT 原生支持请求-响应模式。
|
||||
- **Topic Alias**:用数字 ID 替代重复的 Topic 字符串,大幅减少高频发布场景下的带宽消耗。
|
||||
- **Subscription Identifier**:订阅时携带一个数字标识,Broker 转发消息时会附带该标识,客户端可以快速区分消息来自哪个订阅,无需解析 Topic。
|
||||
- **增强认证(Enhanced Authentication)**:支持 SASL 风格的多步骤认证流程(如 SCRAM、Kerberos),取代 3.1.1 仅有的用户名/密码。
|
||||
|
||||
### Go 代码示例
|
||||
|
||||
@@ -156,6 +211,26 @@ func main() {
|
||||
|
||||
这段代码做了三件事:连接 Broker、订阅 `sensor/temperature` Topic、发布一条温度消息。`OnConnect` 回调确保断线重连后会重新订阅,这是 MQTT 客户端开发的最佳实践。
|
||||
|
||||
### 安全与认证
|
||||
|
||||
MQTT 的安全性通常在三个层面构建:
|
||||
|
||||
1. **传输层加密**:使用 TLS(端口 8883)加密通信链路,防止中间人窃听和篡改。资源极度受限的设备也可以用 TLS-PSK(预共享密钥)降低握手开销。
|
||||
2. **身份认证**:
|
||||
- **用户名/密码**:CONNECT 报文中携带,简单但密码以明文传输(必须配合 TLS)。
|
||||
- **客户端证书**:通过 mTLS 双向认证,Broker 验证客户端证书的合法性,安全性更高。
|
||||
- **MQTT 5.0 增强认证**:支持 SASL 多步骤挑战-响应机制,适用于企业级 SSO 集成。
|
||||
3. **授权(ACL)**:Broker 根据客户端身份(通常是 Client ID 或用户名)控制其可以发布/订阅哪些 Topic。ACL 规则格式因 Broker 实现而异,但核心思想一致——最小权限原则。
|
||||
|
||||
### 主流 Broker 简介
|
||||
|
||||
| Broker | 语言 | 特点 |
|
||||
|--------|------|------|
|
||||
| **EMQX** | Erlang/Elixir | 高性能分布式 Broker,支持百万级并发连接,插件生态丰富,企业级首选 |
|
||||
| **Mosquitto** | C | Eclipse 基金会维护,轻量单机,非常适合嵌入式网关和开发测试 |
|
||||
| **HiveMQ** | Java | 商业 Broker,提供 MQTT 5.0 全量支持和 Kafka/企业系统桥接 |
|
||||
| **VerneMQ** | Erlang | 开源分布式 Broker,基于 LevelDB 持久化,适合中小规模集群 |
|
||||
|
||||
---
|
||||
|
||||
> [!tip] 实践建议
|
||||
|
||||
Reference in New Issue
Block a user