Skip to main content

MQTT 协议技术总结

一、背景:为什么物联网需要 MQTT

1.1 HTTP 在物联网场景下的问题

以温度传感器为例,每 10 秒上报一次,一天约 8,600 多次请求:

问题说明
头部开销过大HTTP 请求头动辄 200~300 字节,而实际数据只有几个字节,头比数据大几十倍
蜂窝网络计费流量走按流量计费的蜂窝网络,浪费严重
频繁建连每次上报都要重建 TCP 连接 + TLS 握手,消耗大量资源
无法服务端主动推送设备通常躲在 NAT 后,没有公网地址;HTTP 只能轮询,绝大多数轮询都是空转

1.2 受限设备(Constrained Devices)的四大约束

MQTT 的每一个设计都是在回应以下约束:

约束具体表现协议层面的回应
算力几块钱单片机,主频几十 MHz,内存几十 KB,跑不动完整 TLS/JSON 解析器报文极小、协议轻量
电量电池供电,要求撑数月到数年;射频模块开启时间是耗电大头少发、发短、长连接避免频繁握手
网络NB-IoT/2G 带宽仅几十 Kbps,丢包率高、延迟上秒,设备在 NAT 后无公网地址双向通信、小报文、QoS 机制
规模一个 Broker 可能同时承载几十万到上百万连接极简协议头、高效转发

二、核心架构:发布/订阅(Publish/Subscribe)

2.1 与 HTTP 请求/响应模式的对比

维度HTTP 点对点MQTT 发布/订阅
通信模式客户端必须知道服务端地址,等待响应所有人只与 Broker 通信
耦合度点对点强耦合发布者与订阅者完全解耦
扩展性新增消费者需修改服务端新增订阅方无需修改发布端

2.2 解耦特性

  • 空间解耦:发布者不知道谁在接收,订阅者不知道是谁发送的。
  • 时间解耦:订阅方离线时,Broker 可暂存消息,上线后再投递(取决于 QoS 和会话设置)。

⚠️ 注意:Broker 成为系统单点,生产环境必须做集群(EMQ X、HiveMQ、VerneMQ 均支持)。集群中订阅关系需要在节点间同步,存在额外成本。


三、Topic 设计

3.1 基本规则

  • Topic 是用 / 分层的字符串,例如:factory/line1/dev7/temp
  • 无需预先定义:发到哪个 Topic,该 Topic 即存在
  • 支持两个通配符:
    • +:匹配单层(可放在任意层级)
    • #:匹配该层级及之后所有层级(只能放在最后一层

3.2 示例

订阅 Topic能收到的消息
factory/line1/+/temp1 号线上所有设备的温度
factory/#整个工厂的所有消息

3.3 设计规范与常见错误

原则说明
层级从大到小区域 → 产线 → 设备 → 指标
固定在前,可变在后不要把设备 ID 放在第一层,否则无法按产线批量订阅
禁止放入可变内容不要在 Topic 中放版本号、时间戳等变化值,会导致通配符失效

四、QoS(服务质量等级)

MQTT 定义了三个投递等级,编号越大不代表越好

QoS名称机制特点适用场景
0最多一次(At Most Once)发完即忘,无确认、无重传开销最小,仅一个报文高频传感器读数,丢一两包无所谓
1至少一次(At Least Once)发送后等待 PUBACK,超时重发可能重复,业务需幂等控制指令
2恰好一次(Exactly Once)四步握手:PUBLISH → PUBREC → PUBREL → PUBCOMP两个 RTT,开销最大极少数强一致性场景

4.1 关键认知:QoS 2 不一定更好

在丢包率高、延迟上秒的弱网环境中:

  • 四步握手意味着两个来回
  • 任何一步丢失都要重来
  • 代价:更长时延、更多电量消耗、更高的整体失败概率

4.2 分段生效机制

QoS 是逐段生效的:

  • 发布者 → Broker:一档
  • Broker → 订阅者:另一档
  • 实际效果取两者的较小值

4.3 选型建议

数据类型推荐 QoS补充措施
遥测数据0无需额外处理
控制指令1业务层做幂等
严格不重复业务层用消息 ID 去重不依赖 QoS 2

五、保留消息(Retained Message)

5.1 机制

  • 发布时设置 retain 标志
  • Broker 单独存储该 Topic 的最后一条保留消息
  • 任何新订阅者上线后,立即收到该消息

5.2 正确使用方式

适用不适用
存储状态:当前开关状态、最新温度读数存储事件流
让新订阅者立即获取最新状态每个 Topic 只保留一条消息,新消息直接覆盖旧消息

六、遗嘱消息(Last Will and Testament)

6.1 机制

  • 设备连接时向 Broker 注册一条"遗嘱"
  • 如果设备异常断开(断网、掉电、崩溃),Broker 自动广播该遗嘱消息
  • 正常断开(发送 DISCONNECT)不会触发遗嘱

6.2 用途

最简单、最轻量的设备在线状态管理方案。例如:遗嘱内容设为 {"status":"offline"},订阅方即可感知设备掉线。


七、持久会话(Persistent Session)

7.1 机制演进

版本机制
MQTT 3.1.1Clean Session 标志
MQTT 5.0Clean Start + Session Expiry Interval
  • 设为 true/启用:每次连接都是全新会话,之前的订阅关系和离线消息全部清空
  • 设为 false/禁用:Broker 通过 Client ID 识别客户端,恢复上次会话

7.2 会话中存储的内容

  1. 订阅关系列表
  2. 离线期间到达的消息(仅 QoS 1 和 QoS 2,QoS 0 不存储

7.3 持久会话的优势

  • 重连后无需重新订阅
  • 可接收断线期间累积的消息

7.4 关键注意事项

问题后果
Client ID 随机生成每次重连都是新会话,持久会话形同虚设
两个设备使用相同 Client IDBroker 会把前一个踢下线;前一个重连又把后一个踢下线 → 无限重连抖动
Client ID 必须与证书绑定验证否则攻击者可用合法证书冒充他人设备 ID 上线

八、Keep Alive 与连接保活

8.1 机制

  • 连接时客户端声明一个秒数(Keep Alive Interval)
  • 承诺在该时间内至少发送一个报文
  • 无数据时发送 PINGREQ,Broker 回复 PINGRESP
  • Broker 在 1.5 倍 Keep Alive 时间内未收到任何报文,判定客户端死亡
  • 判定死亡后:关闭连接 + 触发遗嘱消息

8.2 配置权衡

设置优点缺点
过短更快检测掉线心跳包消耗电量和带宽
过长省电设备掉线后很久才能发现

8.3 实际约束

  • 运营商 NAT 通常会在几分钟内回收空闲连接
  • Keep Alive 必须短于 NAT 回收时间
  • 电池设备:通常几百秒
  • 长供电设备:约 60 秒

九、MQTT vs Kafka:互补而非替代

维度KafkaMQTT
核心定位持久化日志,消息按 offset 顺序落盘,可反复回放消息路由器,转发完即完成使命
连接规模几十到上百个客户端几十万到上百万个连接
客户端体积客户端库几 MB可塞进几十 KB 固件
Topic 特性需预先创建,重资源,几千个就要小心轻量字符串,随手创建,几百万个无压力
消费模型拉(Pull),消费者自主控制进度推(Push),来了即投递

典型架构

[设备] --MQTT--> [Broker] --桥接--> [Kafka] --> [持久化/回放/流式计算]
↑ ↑
海量连接 + 弱网 数据持久化 + 流处理

各干各擅长的事:MQTT 扛连接和弱网,Kafka 管持久化和回放。


十、安全三层模型

物联网安全事故大多出在协议层,必须构建三层防御:

10.1 传输加密

  • MQTT 本身不加密,必须跑在 TLS 上
  • 端口从 1883 升级为 8883
  • 受限设备运行 TLS 吃力时,可选用 ECC 证书(比 RSA 短得多,握手更快)

10.2 身份认证

方案风险推荐度
用户名/密码,批量烧录同一密码一台设备被拆读固件,整批暴露❌ 不推荐
一机一密每台设备出厂烧录独立凭据✅ 推荐
双向 TLS(mTLS)每台设备一张客户端证书,Broker 验设备,设备也验 Broker✅✅ 最强

10.3 权限控制(ACL)

  • 认证解决"你是谁",授权解决"你能干什么"
  • 必须按 Topic 做 ACL
    • 设备只能向自己的 Topic 发布
    • 设备只能订阅自己的命令 Topic
  • 反面教材:被攻破的设备若订阅 #,可窃取整个工厂的数据
  • Client ID 必须与证书绑定并验证,防止证书合法但 ID 被冒充