MQTT笔记

MQTT笔记

    • 一、协议版本
    • 二、核心概念深入讲解
      • [1. 会话 Session](#1. 会话 Session)
      • [2. Topic 主题规则](#2. Topic 主题规则)
      • [3. Retain 保留消息](#3. Retain 保留消息)
      • [4. Will 遗嘱消息(Last Will and Testament)](#4. Will 遗嘱消息(Last Will and Testament))
      • [5. QoS 再细化](#5. QoS 再细化)
    • [三、MQTT 控制报文(一共14种报文)](#三、MQTT 控制报文(一共14种报文))
      • [心跳 KeepAlive](#心跳 KeepAlive)
    • 四、Broker(消息代理)常用开源实现
    • [五、MQTT 安全机制](#五、MQTT 安全机制)
    • [六、MQTT over WebSocket](#六、MQTT over WebSocket)
    • [七、MQTT 优缺点](#七、MQTT 优缺点)
    • 八、适用/不适用场景
    • [九、简单对比 MQTT vs Kafka vs WebSocket](#九、简单对比 MQTT vs Kafka vs WebSocket)
    • [十、MQTT 消息结构(PUBLISH报文)](#十、MQTT 消息结构(PUBLISH报文))
    • [十一、MQTT 时序图](#十一、MQTT 时序图)
      • [QoS2 目的:**保证消息有且仅收到 1 次,不丢、不重复**,一共 4 步:](#QoS2 目的:保证消息有且仅收到 1 次,不丢、不重复,一共 4 步:)
      • [心跳 PINGREQ / PINGRESP(KeepAlive 保活)](#心跳 PINGREQ / PINGRESP(KeepAlive 保活))
        • [1. 两个报文](#1. 两个报文)
        • [2. 作用](#2. 作用)
        • [3. 重点区分](#3. 重点区分)

一、协议版本

目前主流两个版本:

  1. MQTT 3.1.1:最广泛,大量物联网设备、开源 broker(Mosquitto)默认支持,稳定够用。
  2. MQTT 5.0 :增强版,新增大量能力,现在逐步普及:
    • 支持共享订阅(多个客户端分摊同一个主题消息,负载均衡)
    • 消息与订阅携带原因码,方便排查失败原因
    • 主题别名,减少报文体积
    • 支持用户属性(自定义KV附加在消息里)
    • 更好的错误反馈、流量控制、会话管理

选型建议:新项目优先考虑MQTT5;老旧嵌入式设备很多只能跑3.1.1。

二、核心概念深入讲解

1. 会话 Session

客户端连接 Broker 时可以指定 Clean Session(清除会话)

  • CleanSession = true(MQTT3.1.1) / Clean Start = true(MQTT5)
    连接成功后,断开连接就清空会话:订阅关系、未接收消息全部丢弃。临时客户端常用(手机APP临时查看数据)。
  • CleanSession = false / Clean Start = false
    Broker保存会话:客户端掉线后,订阅关系保留;QoS1/QoS2未送达消息会缓存。设备重连后,自动补发离线期间消息。适合传感器这类持续在线设备。

缓存消息有上限,Broker一般会设置最大缓存数量,超出会丢弃。

2. Topic 主题规则

  • 层级分隔符:/,例如 device/001/temp
  • 通配符 (只能用在订阅,发布消息不能带通配符)
    • +:单层通配符。sensor/+/temp 匹配 sensor/room1/temp、sensor/room2/temp;不能跨层级
    • #:多层通配符,必须放在主题末尾。sensor/room1/# 匹配 sensor/room1/temp、sensor/room1/hum

示例:

主题:factory/workshop1/machine01/temp

factory/+/machine01/temp ✔ 匹配

factory/# ✔ 匹配

factory/#/temp ❌ 错误,#只能在最后

3. Retain 保留消息

发布消息时设置 retain=true:

Broker保存这个主题最新一条消息 。

新客户端订阅该主题瞬间,Broker立刻把这条保留消息推送给新订阅者。

场景:上报设备当前状态。比如温度传感器,新APP订阅后马上拿到当前温度,不用等传感器下一次上报。

清除保留消息:向该主题发送一条 payload 为空、retain=true 的消息。

4. Will 遗嘱消息(Last Will and Testament)

客户端连接Broker的时候,预先告诉Broker:

如果我非正常断开(断网、断电、程序崩溃,没有发送正常DISCONNECT包),请帮我向指定主题发送遗嘱消息。

正常调用断开指令DISCONNECT,遗嘱消息不会发送。

场景:设备离线告警。设备掉线,其他订阅端立刻收到 device/001/status = offline。

5. QoS 再细化

QoS 是发布者和Broker之间 的保障,不是端到端!

例如:发布QoS1,订阅QoS0 → Broker收到消息后,发给订阅者就是QoS0。

  • QoS 0:最多一次
    PUBLISH报文直接发出,无应答。丢包就丢失。开销最小。适合高频非关键数据(温度实时流,丢一条无所谓)。
  • QoS 1:至少一次
    流程:PUBLISH → 收到PUBACK才算成功。没收到ACK就重发。
    缺点:消息可能重复到达,业务层需要做去重。适合状态上报,允许重复。
  • QoS 2:恰好一次
    四次握手:PUBLISH → PUBREC → PUBREL → PUBCOMP。
    保证消息只送达一次。开销最大。适合支付、开关指令这类不能丢、不能重复的场景。

三、MQTT 控制报文(一共14种报文)

MQTT通信全部由固定格式报文组成,常见:

  1. CONNECT:客户端发起连接
  2. CONNACK:Broker响应连接结果
  3. SUBSCRIBE:客户端订阅主题
  4. SUBACK:订阅应答
  5. PUBLISH:收发消息主体报文
  6. PUBACK / PUBREC / PUBREL / PUBCOMP:QoS应答报文
  7. UNSUBSCRIBE:取消订阅
  8. UNSUBACK:取消订阅应答
  9. PINGREQ:心跳请求(客户端保活)
  10. PINGRESP:心跳响应
  11. DISCONNECT:客户端正常断开

心跳 KeepAlive

CONNECT的时候设置KeepAlive时间(秒)。

含义:客户端必须在这个时间间隔内至少发一次PINGREQ,证明在线。

如果Broker超过1.5倍KeepAlive时间没收到客户端任何数据 → 判断客户端掉线,触发遗嘱消息。

嵌入式设备常用:KeepAlive=60s。

四、Broker(消息代理)常用开源实现

  1. Eclipse Mosquitto:轻量、简单,MQTT3.1.1/5支持,最常用,适合测试、中小型项目。
  2. EMQX:高性能,百万级连接,MQTT5完善,支持集群、共享订阅、规则引擎,工业/云平台常用。
  3. VerneMQ:高并发集群Broker。

五、MQTT 安全机制

  1. 用户名密码认证:CONNECT包携带username/password,最简单。
  2. TLS/SSL加密:MQTT over TLS(端口8883),传输内容加密,防止抓包窃听;MQTT默认1883明文。
  3. 证书认证(x509):客户端证书,比账号密码更安全,设备场景首选。
  4. 权限控制 ACL:Broker配置,控制客户端只能订阅/发布指定主题(例如设备只能发布自己id的主题,不能读其他设备)。

六、MQTT over WebSocket

端口一般 8083。

把MQTT包封装在WebSocket里,浏览器前端JS直接连接MQTT broker,不用后端中转。网页控制台实时看传感器数据常用。

七、MQTT 优缺点

✅ 优点

  1. 报文极小,网络开销低,适合低带宽、4G/窄带物联网NB-IoT
  2. Pub/Sub解耦,设备不用关心对方IP地址
  3. 长连接,消息实时推送
  4. 丰富特性:遗嘱、保留消息、QoS,适配物联网设备不稳定网络
  5. 跨平台,几乎所有单片机、嵌入式、编程语言都有SDK

❌ 缺点

  1. 依赖Broker,Broker是单点(可以集群解决)
  2. 不适合超大文件传输(设计初衷是短消息,payload最大理论上限256MB,但一般业务只传几十~几百字节)
  3. QoS不能跨Broker保证,分布式场景要业务层兜底
  4. 消息没有内置持久化(Broker可以配置持久化,不是协议强制)

八、适用/不适用场景

✅ 适合:

  • 物联网传感器、智能硬件上报数据
  • 设备远程下发控制指令
  • 实时状态推送、设备上下线告警
  • NB-IoT、4G模组低功耗设备

❌ 不适合:

  • 大文件上传下载(视频流一般不用原生MQTT)
  • 复杂事务消息(kafka更适合大数据流)
  • 一次性短请求(HTTP更简单)

九、简单对比 MQTT vs Kafka vs WebSocket

  1. MQTT:物联网,海量低算力设备,小消息,Pub/Sub,支持QoS/遗嘱。
  2. Kafka:大数据日志流,高吞吐,消息持久化,适合后端数据分析,不适合嵌入式设备。
  3. 原生WebSocket:浏览器双向通信,没有主题、QoS、遗嘱这些能力,只是单纯双向通道。MQTT over WebSocket是在WebSocket之上跑MQTT协议。

十、MQTT 消息结构(PUBLISH报文)

报文分为两部分:固定头 + 可变头 + 载荷payload

  • 固定头:控制报文类型、QoS、retain等标记
  • 可变头:主题名,QoS1/2带报文ID;MQTT5可带用户属性
  • payload:消息内容,二进制字节流,MQTT不关心里面是JSON、文本、二进制,业务自己解析。

习惯:物联网消息大多用JSON,方便调试。

十一、MQTT 时序图

QoS2 目的:保证消息有且仅收到 1 次,不丢、不重复,一共 4 步:

  1. PUBLISH:发布端 → Broker:发送消息
  2. PUBREC:Broker → 发布端:已收到消息(收到这一步,Broker 就不会再接收这条消息的重复 PUBLISH)
  3. PUBREL(Release,释放消息) :发布端 → Broker:我确认收到 PUBREC 了,现在允许 Broker 把这条消息转发出去
  4. PUBCOMP(Complete,发布完成):Broker → 发布端:消息转发流程全部结束,这条报文 ID 可以释放

简单一句话理解这两个报文:

PUBREL = "我确认你收到消息了,可以把消息发出去了"

PUBCOMP = "全部搞定,这条消息事务结束"
这 4 个报文(PUBLISH-PUBREC-PUBREL-PUBCOMP)就是 QoS2 的四次握手。


心跳 PINGREQ / PINGRESP(KeepAlive 保活)

1. 两个报文
  • PINGREQ :Ping Request,心跳请求(客户端发给 Broker)
  • PINGRESP :Ping Response,心跳响应(Broker 回复客户端)
2. 作用

MQTT 是长连接 ,但是网络中间设备(路由器 / 防火墙)会自动删掉长时间没有数据的空闲 TCP 连接。

所以客户端必须定时发心跳包,告诉 Broker:我还在线!

工作规则
  1. 客户端 CONNECT 连接 Broker 的时候,会填写 KeepAlive(保活时间,单位秒,比如 60s)
  2. 规则:客户端必须在 KeepAlive 时间内,至少发一次 PINGREQ
  3. Broker 判断掉线:超过 1.5 × KeepAlive 的时间,没有收到客户端任何数据(PINGREQ 或者消息)
    → Broker 判定客户端异常掉线(断网 / 断电 / 程序崩溃) ,触发遗嘱消息 Will

举例子:KeepAlive=60 秒

客户端每 50s 发送一次 PINGREQ(心跳请求)

Broker 收到,回复 PINGRESP(心跳响应)

如果断网,Broker 等 90s(1.5*60)还没收到任何包 → 判断离线,推送遗嘱消息。

3. 重点区分

✅ 正常下线:客户端主动发送DISCONNECT,Broker 直接知道离线,不触发遗嘱

❌ 异常掉线:没发 DISCONNECT,Broker 靠心跳超时判断离线,触发遗嘱消息


相关推荐
码匠许师傅40 分钟前
【C++三方组件】redis-plus-plus:现代 Redis C++ 客户端
java·c++·redis
二宝哥1 小时前
【问题】代理转发到后端使用request.getRequestURL()地址没有端口
java
霸道流氓气质1 小时前
HashiCorp Vault 与云 KMS 入门实战:动态密钥、SecretLeaseContainer 与 Java 集成完整指南
java·开发语言
wuminyu1 小时前
stackChunkOop内部结构与GC屏障的配合工作原理剖析
java·linux·c语言·jvm·c++
xuxigifxfh1 小时前
牛客:HJ21 简单密码
java·开发语言·算法
承渊政道1 小时前
Linux网络学习【TCP Socket编程实战:connect的断线重连与客户端访问Linux验证】
linux·网络·学习·ubuntu·tcp·socket编程
小π军1 小时前
C 和 C++ 是否都支持函数的重载?
java·c语言·c++
qq_408606861 小时前
确界的倒数性质证明
学习·算法
sunoo-2291 小时前
嵌入式Linux驱动学习笔记(第3-6天):从misc封装、platform总线到设备树、中断与单总线外设实战
linux·arm开发·驱动开发·笔记·单片机·学习