MQTT用着也有好一段时间了,还没写过博文总结,今天来写个快速入门(其实多数是复制黏贴)。当然你也可以直接看文档一网打尽:

https://www.emqx.com/zh/mqtt-guide

MQTT的特点

MQTT是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通信。

主要特点包括:

  • 轻量级:物联网设备通常在处理能力、内存和能耗方面受到限制。MQTT 开销低、报文小的特点使其非常适合这些设备

  • 可靠:互联网网络常常面临高延迟或连接不稳定的情况。MQTT 支持多种 QoS 等级、会话感知和持久连接,即使在困难的条件下也能保证消息的可靠传递,使其非常适合物联网应用。

  • 安全通信:为确保数据在传输过程中的机密性,MQTT 提供TLS和SSL加密功能。此外,MQTT 还通过用户名/密码凭证或客户端证书提供身份验证和授权机制,以保护网络及其资源的访问。

  • 双向通信:MQTT 的发布-订阅模式为设备之间提供了无缝的双向通信方式。客户端既可以向主题发布消息,也可以订阅接收特定主题上的消息,从而实现了物联网生态系统中的高效数据交换,而无需直接将设备耦合在一起。这种模式也简化了新设备的集成,同时保证了系统易于扩展。

  • 连续、有状态的会话:MQTT 提供了客户端与 Broker 之间保持有状态会话的能力,这使得系统即使在断开连接后也能记住订阅和未传递的消息。此外,客户端还可以在建立连接时指定一个保活间隔,这会促使 Broker 定期检查连接状态。如果连接中断,Broker 会储存未传递的消息(根据 QoS 级别确定),并在客户端重新连接时尝试传递它们。这个特性保证了通信的可靠性,降低了因间断性连接而导致数据丢失的风险。

MQTT工作原理

MQTT系统主要包括MQTT客户端和MQTT Broker。

MQTT客户端
任何运行 MQTT 客户端库的应用或设备都是 MQTT 客户端,一般是众多的物联网设备。

MQTT Broker
MQTT Broker 是负责处理客户端请求的关键组件,包括建立连接、断开连接、订阅和取消订阅等操作,同时还负责消息的转发。类似于服务器的角色。

发布-订阅模式
发布-订阅模式与客户端-服务器模式的不同之处在于,它将发送消息的客户端(发布者)和接收消息的客户端(订阅者)进行了解耦。发布者和订阅者之间无需建立直接连接,而是通过 MQTT Broker 来负责消息的路由和分发。也就是说每一个客户端既可以是发送者也可以是接收者,发送者发送的报文通过MQTT Broker广播出去。

MQTT 协议根据主题来转发消息。主题通过 / 来区分层级,类似于 URL 路径,例如:

chat/room/1
sensor/10/temperature
sensor/+/temperature

发布者将对应Topic的报文发送导Broker,然后Broker将报文广播出去,然后订阅者就会接收对应Topic的报文。MQTT主题支持以下两种通配符:+#

MQTT Topic详解

MQTT 主题本质上是一个 UTF-8 编码的字符串,是 MQTT 协议进行消息路由的基础。MQTT 主题类似 URL 路径,使用斜杠 / 进行分层:

chat/room/1
sensor/10/temperature
sensor/+/temperature
sensor/#

为了避免歧义且易于理解,通常不建议主题以 / 开头或结尾,例如 /chat 或 chat/

不同于消息队列中的主题(比如 Kafka 和 Pulsar),MQTT 主题不需要提前创建。MQTT 客户端在订阅或发布时即自动的创建了主题,开发者无需再关心主题的创建,并且也不需要手动删除主题。

单层通配符
加号 (“+” U+002B) 是用于单个主题层级匹配的通配符。在使用单层通配符时,单层通配符必须占据整个层级

多层通配符
井字符号是用于匹配主题中任意层级的通配符。多层通配符表示它的父级和任意数量的子层级,在使用多层通配符时,它必须占据整个层级并且必须是主题的最后一个字符。比如sensor/#

MQTT的工作流程

在了解了 MQTT 的基本组件之后,让我们来看看它的一般工作流程:

  1. 客户端使用 TCP/IP 协议与 Broker 建立连接,可以选择使用 TLS/SSL 加密来实现安全通信。客户端提供认证信息,并指定会话类型(Clean Session 或 Persistent Session)。
  2. 客户端既可以向特定主题发布消息,也可以订阅主题以接收消息。当客户端发布消息时,它会将消息发送给 MQTT Broker;而当客户端订阅消息时,它会接收与订阅主题相关的消息。
  3. MQTT Broker 接收发布的消息,并将这些消息转发给订阅了对应主题的客户端。它根据 QoS 等级确保消息可靠传递,并根据会话类型为断开连接的客户端存储消息。

MQTT 连接由客户端向Broker 发起。客户端与Broker 建立网络连接后,需要先发送一个 CONNECT 数据包给Broker。Broker 收到 CONNECT 包后会回复一个 CONNACK 给客户端,客户端收到 CONNACK 包后表示 MQTT 连接建立成功。如果客户端在超时时间内未收到服务器的 CONNACK 数据包,就会主动关闭连接。大多数场景下,MQTT 通过 TCP/IP 协议进行网络传输,但是 MQTT 同时也支持通过 WebSocket 或者 UDP 进行网络传输。

MQTT 连接参数的使用

下面盘一盘MQTT连接需要使用的参数

1.连接地址

MQTT 的连接地址通常包含 :服务器 IP 或者域名、服务器端口、连接协议。

基于 TCP 的 MQTT 连接
mqtt 是普通的 TCP 连接,端口一般为 1883。

mqtts 是基于 TLS/SSL 的安全连接,端口一般为 8883。

比如 mqtt://broker.emqx.io:1883 是一个基于普通 TCP 的 MQTT 连接地址。

基于 WebSocket 的连接
ws 是普通的 WebSocket 连接,端口一般为 8083。

wss 是基于 WebSocket 的安全连接,端口一般为 8084。

当使用 WebSocket 连接时,连接地址还需要包含 Path, EMQX 默认配置的 Path 是 /mqtt。比如ws://broker.emqx.io:8083/mqtt 是一个基于 WebSocket 的 MQTT 连接地址。

2.客户端ID

MQTT 服务器使用 Client ID 识别客户端,连接到服务器的每个客户端都必须要有唯一的 Client ID。Client ID 的长度通常为 1 至 23 个字节的 UTF-8 字符串。

3.连接超时(Connect Timeout)

设置连接超时时长,收到服务器连接确认前的等待时间,等待时间内未收到连接确认则为连接失败。

4.保活周期(Keep Alive)

保活周期,是一个以秒为单位的时间间隔。客户端在无报文发送时,将按 Keep Alive 设定的值定时向服务端发送心跳报文,确保连接不被服务端断开。

在连接建立成功后,如果服务器没有在 Keep Alive 的 1.5 倍时间内收到来自客户端的任何包,则会认为和客户端之间的连接出现了问题,此时服务器便会断开和客户端的连接。

5.清除会话(Clean Session)

为 false 时表示创建一个持久会话,在客户端断开连接时,会话仍然保持并保存离线消息,直到会话超时注销。为 true 时表示创建一个新的临时会话,在客户端断开时,会话自动销毁。持久会话避免了客户端掉线重连后消息的丢失,并且免去了客户端连接后重复的订阅开销。这一功能在带宽小,网络不稳定的物联网场景中非常实用。

MQTT 5.0 中将 Clean Session 拆分成了 Clean Start 与 Session Expiry Interval。

Clean Start 用于指定连接时是创建一个全新的会话还是尝试复用一个已存在的会话。为 true 时表示必须丢弃任何已存在的会话,并创建一个全新的会话;为 false 时表示必须使用与 Client ID 关联的会话来恢复与客户端的通信(除非会话不存在)。

Session Expiry Interval 用于指定网络连接断开后会话的过期时间。设置为 0 或未设置,表示断开连接时会话即到期;设置为大于 0 的数值,则表示会话在网络连接关闭后会保持多少秒;设置为 0xFFFFFFFF 表示会话永远不会过期。

6.遗嘱消息(Last Will)

遗嘱消息是 MQTT 为那些可能出现意外断线的设备提供的将遗嘱优雅地发送给其他客户端的能力。设置了遗嘱消息消息的 MQTT 客户端异常下线时,MQTT 服务器会发布该客户端设置的遗嘱消息。

意外断线包括:因网络故障,连接被服务端关闭;设备意外掉电;设备尝试进行不被允许的操作而被服务端关闭连接等

遗嘱消息可以看作是一个简化版的 MQTT 消息,它也包含 Topic、Payload、QoS、Retain 等信息。

  • 当设备意外断线时,遗嘱消息将被发送至遗嘱 Topic;
  • 遗嘱 Payload 是待发送的消息内容;
  • 遗嘱 QoS 与普通 MQTT 消息的 QoS 一致,详细请见MQTT QoS(服务质量)介绍。
  • 遗嘱 Retain 为 true 时表明遗嘱消息是保留消息

QoS机制详解

QoS
MQTT 提供了三种服务质量(QoS),在不同网络环境下保证消息的可靠性。

  • QoS 0:消息最多传送一次。如果当前客户端不可用,它将丢失这条消息。
  • QoS 1:消息至少传送一次。
  • QoS 2:消息只传送一次。

其中,使用 QoS 0 可能丢失消息,使用 QoS 1 可以保证收到消息,但消息可能重复,使用 QoS 2 可以保证消息既不丢失也不重复。QoS 等级从低到高,不仅意味着消息可靠性的提升,也意味着传输复杂程度的提升。

此外,在报文中还有一个DUP标志,用于标识该报文被发送了几次,比如DUP=0表示是第一次发送

QoS 0-最多交付一次

QoS 0 是最低的 QoS 等级。QoS 0 消息即发即弃,不需要等待确认,不需要存储和重传,因此对于接收方来说,永远都不需要担心收到重复的消息。

QoS 1-至少交付一次

为了保证消息到达,QoS 1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以后,才能认为消息投递成功,在此之前,发送方需要存储该 PUBLISH 报文以便下次重传。QoS 1 需要在 PUBLISH 报文中设置 Packet ID,而作为响应的 PUBACK 报文,则会使用与 PUBLISH 报文相同的 Packet ID,以便发送方收到后删除正确的 PUBLISH 报文缓存。
在这里插入图片描述

但是QoS 1有可能会重复,这是因为对于接收方来说,可能存在以下两种情况:
在这里插入图片描述
第一种情况,接收方的应答PUBACK 报文丢失,而重传了 PUBLISH 报文。此时,接收方收到的前后两个 PUBLISH 报文使用了相同的 Packet ID,并且第二个 PUBLISH 报文的 DUP 标志为 1,此时它确实是一个重复的消息。

第二种情况,第一个 PUBLISH 报文已经完成了投递,1024 这个 Packet ID 重新变为可用状态。发送方使用这个 Packet ID 发送了一个全新的 PUBLISH 报文,但这一次报文未能到达对端,所以发送方后续重传了这个 PUBLISH 报文。这就使得虽然接收方收到的第二个 PUBLISH 报文同样是相同的 Packet ID,并且 DUP 为 1,但确实是一个全新的消息。

由于我们无法区分这两种情况,所以只能让接收方将这些 PUBLISH 报文都当作全新的消息来处理。因此当我们使用 QoS 1 时,消息的重复在协议层面上是无法避免的。

QoS 2-只交付一次

QoS 2 解决了 QoS 0、1 消息可能丢失或者重复的问题,但相应地,它也带来了最复杂的交互流程和最高的开销。每一次的 QoS 2 消息投递,都要求发送方与接收方进行至少两次请求/响应流程。

  1. 首先,发送方存储并发送 QoS 为 2 的 PUBLISH 报文以启动一次 QoS 2 消息的传输,然后等待接收方回复 PUBREC 报文。这一部分与 QoS 1 基本一致,只是响应报文从 PUBACK 变成了 PUBREC。

  2. 当发送方收到 PUBREC 报文,即可确认接收方已经收到了 PUBLISH 报文,发送方将不再需要重传这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地存储的 PUBLISH 报文,然后发送一个 PUBREL 报文,通知对端自己准备将本次使用的 Packet ID 标记为可用了。与 PUBLISH 报文一样,我们需要确保 PUBREL 报文到达对端,所以也需要一个响应报文,并且这个 PUBREL 报文需要被存储下来以便后续重传。

  3. 当接收方收到 PUBREL 报文,也可以确认在这一次的传输流程中不会再有重传的 PUBLISH 报文到达,因此回复 PUBCOMP 报文表示自己也准备好将当前的 Packet ID 用于新的消息了。

  4. 当发送方收到 PUBCOMP 报文,这一次的 QoS 2 消息传输就算正式完成了。在这之后,发送方可以再次使用当前的 Packet ID 发送新的消息,而接收方再次收到使用这个 Packet ID 的 PUBLISH 报文时,也会将它视为一个全新的消息。

与 QoS 1 相比,QoS 2 新增了 PUBREL 报文和 PUBCOMP 报文的流程,也正是这个新增的流程带来了消息不会重复的保证。QoS 2 规定,发送方只有在收到 PUBREC 报文之前可以重传 PUBLISH 报文。一旦收到 PUBREC 报文并发出 PUBREL 报文,发送方就进入了 Packet ID 释放流程,不可以再使用当前 Packet ID 重传 PUBLISH 报文。同时,在收到对端回复的 PUBCOMP 报文确认双方都完成 Packet ID 释放之前,也不可以使用当前 Packet ID 发送新的消息。

与 QoS 1 相比,QoS 2 新增了 PUBREL 报文和 PUBCOMP 报文的流程,也正是这个新增的流程带来了消息不会重复的保证。其实,消息去重的关键就在于,通信双方如何正确地同步释放 Packet ID,换句话说,不管发送方是重传消息还是发布新消息,一定是和对端达成共识了的。而 QoS 2 中增加的 PUBREL 流程,正是提供了帮助通信双方协商 Packet ID 何时可以重用的能力。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部