MQTT概念篇

MQTT(Message Queuing Telemetry Transport)

本篇文章是基于MQTT 5.0版本编写,部分特性在3.1和3.1.1等版本中可能未启用。 MQTT是一种轻量级、基于发布-订阅模式的消息传输协议,适用于资源受限的设备和低带宽、高延迟或不稳定的网络环境。它在物联网应用中广受欢迎,能够实现传感器、执行器和其它设备之间的高效通信。
本篇文章部分内容摘自 中文网站 / 官网

为什么MQTT 是适用于物联网的最佳协议?
MQTT所具有的适用于物联网特定需求的特点和功能,使其成为物联网领域最佳的协议之一,它的主要特点包括:

  1. 轻量级:物联网设备通常在处理能力、内存和能耗方面受到限制。MQTT 开销低、报文小的特点使其非常适合这些设备,因为它消耗更少的资源,即使在有限的能力下也能实现高效的通信。
  2. 可靠:物联网网络常常面临高延迟或连接不稳定的情况。MQTT 支持多种 QoS 等级、会话感知和持久连接,即使在困难的条件下也能保证消息的可靠传递,使其非常适合物联网应用。
  3. 安全通信:安全对于物联网网络至关重要,因为其经常涉及敏感数据的传输。为确保数据在传输过程中的机密性,MQTT 提供传输层安全(TLS)和安全套接层(SSL)加密功能。此外,MQTT 还通过用户名/密码凭证或客户端证书提供身份验证和授权机制,以保护网络及其资源的访问。
  4. 双向通信:MQTT 的发布-订阅模式为设备之间提供了无缝的双向通信方式。客户端既可以向主题发布消息,也可以订阅接收特定主题上的消息,从而实现了物联网生态系统中的高效数据交换,而无需直接将设备耦合在一起。这种模式也简化了新设备的集成,同时保证了系统易于扩展。
  5. 连续、有状态的会话:MQTT 提供了客户端与 Broker 之间保持有状态会话的能力,这使得系统即使在断开连接后也能记住订阅和未传递的消息。此外,客户端还可以在建立连接时指定一个保活间隔,这会促使 Broker 定期检查连接状态。如果连接中断,Broker 会储存未传递的消息(根据 QoS 级别确定),并在客户端重新连接时尝试传递它们。这个特性保证了通信的可靠性,降低了因间断性连接而导致数据丢失的风险。
  6. 大规模物联网设备支持:物联网系统往往涉及大量设备,需要一种能够处理大规模部署的协议。MQTT 的轻量级特性、低带宽消耗和对资源的高效利用使其成为大规模物联网应用的理想选择。通过采用发布-订阅模式,MQTT 实现了发送者和接收者的解耦,从而有效地减少了网络流量和资源使用。此外,协议对不同 QoS 等级的支持使得消息传递可以根据需求进行定制,确保在各种场景下获得最佳的性能表现。
  7. 语言支持:物联网系统包含使用各种编程语言开发的设备和应用。MQTT 具有广泛的语言支持,使其能够轻松与多个平台和技术进行集成,从而实现了物联网生态系统中的无缝通信和互操作性。您可以阅读我们的 MQTT 客户端编程系列文章,学习如何在 PHP、Node.js、Python、Golang、Node.js 等编程语言中使用 MQTT。

基础概念

  1. Client:
    客户端,即所有连接MQTT服务器的连接方都是客户端,不管这个连接方是发送消息,订阅消息的都可以叫做客户端。
  2. Broker:
    服务端,我的理解就是MQTT的服务器,用来管理客户端连接,以及接收和分发消息到客户端
  3. 发布/订阅模式:
    发布-订阅模式与客户端-服务器模式的不同之处在于,它将发送消息的客户端(发布者)和接收消息的客户端(订阅者)进行了解耦。发布者和订阅者之间无需建立直接连接,而是通过MQTT Broker来负责消息的路由和分发。
  4. 主题:
    MQTT协议根据主题来转发消息。主题通过 / 来区分层级,类似于URL路径,例如:
    chat/room/1
    sensor/10/temperature
    sensor/+/temperature
  5. QoS:
    MQTT提供了三种服务质量(QoS),在不同网络环境下保证消息的可靠性。
    QoS 0:消息最多传送一次。如果当前客户端不可用,它将丢失这条消息。
    QoS 1:消息至少传送一次。
    QoS 2:消息只传送一次。
Notes: QoS降级 
消息发布方和消息订阅方的QoS不一致,会导致消费端的QoS降级,QoS降级单指订阅单指消费端,消息发布端不存在消息降级的问题。   
消费端在消费时要求Broker可以向其转发的消息的最大QoS等级为QoS1,那么后续所有QoS2消息都会降级至QoS1转发给此消费者,而所有QoS0和QoS1消息则会保持原始的QoS等级转发。

服务端最终授予的最大 QoS = min ( 服务端支持的最大 QoS, 客户端请求的最大 QoS )
  1. MQTT会话
    MQTT会话本质上就是一组需要服务端和客户端额外存储的上下文数据,这些数据可以仅持续与网络连接一样长的时间,也可以跨越多个连续的网络连接存在。当客户端与服务端借助这些会话数据恢复通信时,可以让网络中断就像从未发生过一样。

MQTT为服务端和客户端分别定义了它们需要存储的会话状态。对于服务端来说,它需要存储以下内容:

  1. 会话是否存在。
  2. 客户端的订阅列表。
  3. 已发送给客户端,但是还没有完成确认的 QoS 1 和 QoS 2 消息。
  4. 等待传输给客户端的 QoS 0 消息(可选),QoS 1 和 QoS 2 消息。
  5. 从客户端收到的,但是还没有完成确认的 QoS 2 消息。
  6. 遗嘱消息与遗嘱过期间隔。
  7. 会话过期时间。

对于客户端来说,它需要存储以下内容:

  1. 已发送给服务端,但是还没有完成确认的 QoS 1 和 QoS 2 消息。
  2. 从服务端收到的,但是还没有完成确认的 QoS 2 消息。

显而易见的是,让服务端和客户端永久存储这些会话数据,不仅会带来很多额外的存储成本,而且在很多场景中也没有必要。譬如我们只是为了避免网络连接短暂中断导致的消息丢失,那么一般将会话数据设置为在连接断开后保留短暂的几分钟即可。
另外,当客户端与服务端会话状态不一致时,比如客户端设备因为重启导致会话数据丢失,那么它需要在连接时告知服务端丢弃原有的会话并创建一个全新的会话。

  1. MQTT报文分析
MQTT报文分析,协议报文包含三个部分,分别是Fixed header / Variable header / Payload
Fixed header,只有两个字节,所有报文都包含
+-----+-----+-----+-----+-----+------+------+------+-------+
|     |     |     |     |     |      |      |      |       |
| Bit |  7  |  6  |  5  |  4  |  3   |  2   |  1   |  0    |
+-----------+-----+-----+------------+------+------+-------+
|     |                       |                            |
|byte1|MQTT ControlPacket type|          Flags             |
+----------------------------------------------------------+
|     |                                                    |
|byte2|          Remaining Length                          |
+----------------------------------------------------------+

进阶概念

1. 主题通配符的使用

+:表示单层通配符,例如 a/+ 匹配 a/x 或 a/y。
#:表示多层通配符,例如 a/# 匹配 a/x、a/b/c/d。

2. 保留消息(Retained Message)

发布者发布消息时,如果 Retained 标记被设置为 true,则该消息即是 MQTT 中的保留消息(Retained Message)。MQTT服务器会为每个主题存储最新一条保留消息,以方便消息发布后才上线的客户端在订阅主题时仍可以接收到该消息。
服务器只会为每个主题保存最新一条保留消息,保留消息的保存时间与服务器的设置有关。若服务器设置保留消息存储在内存,则 MQTT服务器重启后消息即会丢失;若存储在磁盘,则服务器重启后保留消息仍然存在。
应用场景:

  1. 智能家居设备的状态只有在变更时才会上报,但是控制端需要在上线后就能获取到设备的状态;
  2. 传感器上报数据的间隔太长,但是订阅者需要在订阅后立即获取到最新的数据;
  3. 传感器的版本号、序列号等不会经常变更的属性,可在上线后发布一条保留消息告知后续的所有订阅者。

3. 遗嘱消息(Will Message)

遗嘱消息是MQTT协议中的一个重要功能,它解决了只有服务端才能知道客户端是否在线的问题,使我们能够为意外离线的客户端优雅地完成善后事宜。遗嘱消息是MQTT为那些可能出现意外断线的设备提供的将遗嘱优雅地发送给第三方的能力。
我们需要明白的是遗嘱消息是客户端事先发给服务端,服务端在有限时间内保存的一条特殊消息,也就是先立遗嘱,那什么时候公布遗嘱呢?如果服务端检测到发送这条遗嘱消息的客户端挂了,就会往指定Topic发送事先约定好的遗嘱消息。

客户端意外断线包括但不限于以下情况

  1. 因网络故障或网络波动,设备在保持连接周期内未能通讯,连接被服务端关闭。
  2. 设备意外掉电。
  3. 设备尝试进行不被允许的操作而被服务端关闭连接,例如订阅自身权限以外的主题等。
    遗嘱消息在客户端发起连接时指定,它和 Client ID、Clean Start 这些字段一起包含在客户端发送的 CONNECT 报文中。

如何判断客户端是不是挂了

  1. 简单起见,只要网络连接在服务端没有收到 Reason Code 为 0x00 的 DISCONNECT 报文的情况下关闭,那么服务端都需要发送遗嘱消息。
  2. 当客户端完成了预定的工作准备正常下线时,可以发送一个 Reason Code 为 0x00 的 DISCONNECT 报文然后关闭网络连接,避免服务端因此发布遗嘱消息。

为啥遗嘱信息会被频繁发送
默认情况下,服务端总是在网络连接意外关闭时立即发布遗嘱消息。但是很多时候,网络连接的中断是短暂的,所以客户端往往能够重新连接并继续之前的会话。这导致遗嘱消息可能被频繁地且无意义地发送。

怎么解决遗嘱消息频繁发送问题

  1. MQTT 5.0专门为遗嘱消息增加了一个Will Delay Interval属性,这个属性决定了服务端检测到客户端网络连接关闭后延迟多久发布遗嘱消息,并以秒为单位。
  2. 如果没有指定Will Delay Interval或者将其设置为0,服务端将仍然在检测到客户端网络连接关闭时立即发布遗嘱消息。
    但如果将 Will Delay Interval 设置为一个大于 0 的值,并且客户端能够在 Will Delay Interval 到期前恢复连接,那么该遗嘱消息将不会被发布。

为什么遗嘱消息可能会丢
遗嘱消息是服务端会话状态的一部分,当会话结束,遗嘱消息也无法继续单独存在。这就是引申出MQTT另外一个比较重要的概念那就是持久会话
如果会话设置不过期,那么遗嘱消息就不会丢,我可真是个大聪明(*ω),那就存在一个问题既然有持久会话,那肯定也有不持久会话也就是会话过期,
我们需要知道的是这个会话过期是可以设置的,那就引申出一个问题在遗嘱消息延迟发布期间,会话可能过期,也可能因为客户端在新的连接中设置Clean Start为1,所以服务端需要丢弃之前的会话。
为了避免丢失遗嘱,此时服务端必须发布该遗嘱消息,即便Will Delay Interval还没有到期。
简单来说就是遗嘱消息会不会丢,取决于Will Delay Interval到期和会话结束这两种情况谁先发生,如果Will Delay Interval先到那遗嘱消息不会丢,如果会话先结束,那万事皆休,遗嘱消息也会丢。

为什么没有收到遗嘱消息
遗嘱消息的延迟发布和取消发布让订阅端最终是否会收到遗嘱消息这个问题变得稍显复杂。

  1. 连接意外关闭且 Will Delay Interval 等于 0,遗嘱消息将在网络连接关闭时立即发布
  2. 连接意外关闭且 Will Delay Interval 大于 0,遗嘱消息将被延迟发布,最大延迟时间取决于 Will Delay Interval 与 Session Expiry Interval 谁先到期:
    1. 客户端未能在 Will Delay Interval 或 Session Expiry Interval 到期前恢复连接,遗嘱消息将被发布。
    2. 在 Will Delay Interval 或 Session Expiry Interval 到期前
      1. 客户端指定 Clean Start 为 0 恢复连接,遗嘱消息将不会被发布。
      2. 客户端指定 Clean Start 为 1 恢复连接,遗嘱消息将因为 现有会话结束 而被立即发布。

如果现有网络连接尚未断开,但客户端使用相同 Client ID发起新的连接,服务端会向现有的网络连接发送一个 Reason Code 为 0x8E(Session Taken Over)的 DISCONNECT 报文然后关闭它。这种情况在网络不佳时非常容易出现,但也属于连接意外关闭。
现在,请思考这样一个问题:如果现有的网络连接的 Session Expiry Interval 等于 0,Will Delay Interval 大于 0,那么当客户端指定 Clean Start 为 0 发起新的网络连接时服务端是否会发送遗嘱消息?
答案是遗嘱消息将在现有的网络连接断开时被立即发布。当服务端关闭现有的网络连接,由于 Session Expiry Interval 为 0,会话也将立即结束。虽然 Clean Start 设置为 0,但服务端将为新的网络连接创建了一个新的会话。所以遗嘱消息将因为会话结束而被发布,即满足了上面所列情形中的 2.1 而不是 2.2.1。。

遗嘱消息使用小技巧
可以与保留消息一起使用。服务端一旦发布了遗嘱消息,就会将它从会话中删除。如果关心此遗嘱消息的客户端不在线,那么它就错过了这条遗嘱消息。
为了避免这种情况,我们可以将遗嘱消息设置为保留消息,这样遗嘱消息在被发布后,还会以保留消息的形式存储在服务端中,客户端可以在任何时候获取这条遗嘱消息。
如果更进一步,可以实现检测客户端设备是否在线。让客户端 myclient 在每次连接时都指定一个主题为 myclient/status,内容为 offline 并且设置了 Will Retain 标志的遗嘱消息。每当连接成功,就向主题 myclient/status 发布一个内容为 online 的保留消息。这样,我们就可以随时订阅主题 myclient/status,来获取客户端 myclient 的最新状态。

对上面做法的进一步解释,利用的是保留消息的特性,即每一个Topic只会留存最新唯一一条保留消息。上面的原理是客户端会在连接服务端的时候发送一条保留消息类型的遗嘱消息,遗嘱信息的内容表明客户端挂了,
如果客户端成功连接到服务端,那么就会用新的保留消息,即客户端活着的保留消息,覆盖之前的保留消息,这样通过订阅指定的Topic就能判断客户端是在线还是离线
这个方法可能存在一个问题,保留类型的遗嘱消息会不会被新的保留消息覆盖?这个解决方法需要验证以下,之后会告知结果。
如果不会覆盖,那不就存在两条保留类型的消息了,于保留消息的特性冲突。
如果会覆盖,那保存客户端离线状态的遗嘱消息就没了,那就没有办法在客户端异常下线的时候发送遗嘱消息,那监控客户端状态也就无从谈起,因为服务端只保留了控客户端活着的保留消息。

现在给出上面问题的结论:
一个客户端连接到服务端并设置了一个保留类型的遗嘱消息,随后该客户端又发布了一个普通的保留消息到同一个主题。如果客户端正常工作并保持连接,那么普通的保留消息会被新订阅者接收到。
然而,如果客户端异常断开连接,代理会发布遗嘱消息,并且,如果遗嘱消息被设置为保留消息,它将替换掉之前的普通保留消息。
因此,保留类型的遗嘱消息和保留消息可以共存,但是它们对同一个主题的最终状态有不同的影响,取决于客户端是正常断开连接还是异常断开连接。正常断开时,最后发布的普通保留消息决定了主题的最新状态;而异常断开时,遗嘱消息(如果被设置为保留)将成为该主题的最新状态。

另外一种客户端状态检测机制就是定制一个客户端心跳包Topic,定时往这个Topic发送客户端心跳数据,以此来完成客户端状态检测。

4. 共享订阅(shared-subscription)

解决了消费端无法短时间消费大量数据而崩溃的问题,实现了消费端的负载均衡。在普通的订阅中,我们每发布一条消息,所有匹配的订阅端都会收到该消息的副本。当某个订阅端的消费速度无法跟上消息的生产速度时,我们没有办法将其中一部分消息分流到其他订阅端中来分担压力。这使订阅端容易成为整个消息系统的性能瓶颈。所以 MQTT 5.0 引入了共享订阅特性,它使得 MQTT 服务端可以在使用特定订阅的客户端之间均衡地分配消息负载。这表示,当我们有两个客户端共享一个订阅时,那么每个匹配该订阅的消息都只会有一个副本投递给其中一个客户端。

共享订阅的核心在于服务端如何在客户端之间分配消息负载。比较常见的负载均衡策略有以下几种:

  1. 随机(Random),在共享订阅组内随机选择一个会话发送消息。
  2. 轮询(Round Robin),在共享订阅组内按顺序选择一个会话发送消息,循环往复。
  3. 哈希(Hash),基于某个字段的哈希结果来分配。
  4. 粘性(Sticky),在共享订阅组内随机选择一个会话发送消息,此后保持这一选择,直到该会话结束再重复这一过程。
  5. 本地优先(Local),随机选择,但优先选择与消息的发布者处于同一节点的会话,如果不存在这样的会话,则退化为普通的随机策略。
  • 35
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值