Java零基础入门物联网(4):mqtt协议详解

3 阅读21分钟

前言:MQTT 的学习就把一些知识点罗列下吧,因为并不需要细化到报文层面,在应用上把这些搞定就能应付大部分业务场景了。

1、MQTT

1.1 简介

MQTT 是一种基于发布订阅模式的轻量级通讯协议,该协议构建于 TCP/IP 上,MQTT 最大优点在于可以用极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。

一个使用 MQTT 协议的应用程序或设备,它总是建立到服务器的网络连接。

客户端可以:

  • 1、发布其他客户端可能会订阅的信息
  • 2、订阅其他客户端发布的消息
  • 3、退订或删除应用程序的消息
  • 4、断开与服务器的连接

MQTT 服务器称为 broker,消息代理,可以是一个应用程序或一台设备,它位于消息发布和订阅者之间,它可以:

  • 1、接收来自客户端的网络连接
  • 2、接收客户端发布的消息
  • 3、处理客户端的订阅和退订请求

而发布者和订阅者都属于客户端,通过中间的 broker 进行交互,它们并不知道对方的存在。

常见的 broker 就是 EMQX,这个可以安装一个来玩玩。

官网:www.emqx.com/zh

客户端好用的工具: MQTTX

官网:mqttx.app/zh

通过模拟两个 MQTTX 和 Broker 的通信,就可以看到消息的订阅和发送,这块比较基础。

1.2 设计原则

MQTT 遵循以下设计原则:

  • 1、精简,不添加可有可无的功能
  • 2、发布订阅模式,方便消息在传感器之间传递
  • 3、允许用户动态创建主题,零运维成本
  • 4、把传输量降到最低提高传输效率
  • 5、支持连续的会话保持和控制
  • 6、提供陪你服务质量公园里

同时,具备以下几个明显的特点:

1、轻量级:mqtt开销低,报文小,适合物联网设备,在有限的资源下也能实现高效的通信

2、可靠:物联网环境经常面临高延迟和连接不稳定的情况。mqtt 支持多种Qos等级,会话感知和持久连接,能够极大保证消息的可靠性传输

3、安全通信:提供ssl认证,具备安全性选择

4、双向通信

1.3 协议方法

在 MQTT 协议中,一个 MQTT 数据包由:固定头(Fiexed header)、可变头(Variable header)、消息体(payload)三部分构成。

MQTT 数据包结构如下:

  • 固定头(Fixed header)。存在于所有 MQTT 数据包中,表示数据包类型及数据包的分组类标识;
  • 可变头(Variable header)。存在于部分 MQTT 数据包中,数据包类型决定了可变头是否存在及其具体内容。可变头部不是可选的意思,而是指这部分在有些协议类型中存在,在有些协议中不存在。
  • 消息体:存在部分 MQTT 数据包中,表示客户端收到的具体内容。与可变头一样,在有些协议类型中有消息内容,有些协议类型中没有消息内容。

协议方法如下

1. CONNECT:客户端向服务器发起连接请求。
2. CONNACK:服务器对CONNECT请求进行回复。
3. PUBLISH:客户端或服务器发布消息。
4. PUBACK:对PUBLISH消息进行确认。
5. PUBREC:对PUBLISH消息进行接收确认。
6. PUBREL:对PUBREC消息进行释放确认。
7. PUBCOMP:对PUBREL消息进行确认。
8. SUBSCRIBE:客户端向服务器订阅主题。
9. SUBACK:服务器对SUBSCRIBE请求进行回复。
10. UNSUBSCRIBE:客户端向服务器取消订阅主题。
11. UNSUBACK:服务器对UNSUBSCRIBE请求进行回复。
12. PINGREQ:客户端向服务器发送心跳请求。
13. PINGRESP:服务器对PINGREQ请求进行回复。
14. DISCONNECT:客户端向服务器发送断开连接请求。

2、Qos 详解

2.1 QOS 介绍

MQTT提供了QoS机制,其核心是设计了多种消息交互机制来提供不同的服务质量,来满足用户在各种场景下对消息可靠性的要求。

MQTT 定义了三个 QoS 等级,分别为:

  • QoS 0,最多交付一次 -----> 可能丢失消息
  • QoS 1,至少交付一次 -----> 可以保证收到消息,但消息可能重复
  • QoS 2,只交付一次 -----> 可以保证消息既不丢失也不重复

Qos 等级是由发布者在 PUBLISH 报文中指定的,大部分情况下 Broker 向订阅者转发消息时都会维持原始的 Qos 不变,不过也有一些例外的情况,根据订阅者的订阅要求。

消息的 Qos 等级可能会在转发的时候发生降级。

例如,订阅者在订阅时要求 Broker 可以向其转发的消息最大 Qos 等级为 Qos 1,那么后续所有 Qos2 的消息都会降级到 Qos1 转发给此订阅者,而所有 Qos 0 和 Qos 1消息。

则会保持原始的 Qos 等级转发

2.2 Qos-0 原理介绍

2.2.1 通讯原理介绍

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

涉及到的相关报文:

2.2.2 Qos 0 消息丢失原因

当我们使用 Qos 0 传递消息时,消息的可靠性完全依赖于底层的 TCP 协议,而 TCP 只能保证在连续稳定不关闭的情况下消息的可靠到达,一旦出现连接关闭,重置,仍有可能丢失当前处于网络链路或操作系统底层缓冲区中的消息,这也是 Qos 0 消息最主要的丢失场景。

2.3 QOS 1 原理介绍

为了保证消息到达,Qos1 加入了应答与重传机制,发送方只有在收到接收方的 PUBACK 报文以后,才认为消息投递成功,在此之前,发送方需要存储该 PUBLISH 报文以便下次重传。

  • Publish
  • puback

QoS 1需要在 PUBLISH 报文中设置 Packet ID,而作为响应的 PUBACK 报文,则会使用与 PUBLISH 报文相同的 Packet ID,以便发送方收到后删除正确PUBLISH 报文缓存。

那为什么会重复呢?

对于发送方来说,没收到 PUBACK 报文分为以下两种情况:

  • 1、PUBLISH 未到达接收方
  • 2、PUBLISH 已经到达接收方,接收方的 PUBACK 报文还未到达发送方

在第一种情况下,发送方虽然重传了 PUBLISH 报文,但是对于接收方来说,实际上仍然只收到一次消息。

在第二种情况下,在发送方重传时,接收方已经收到过了这个 PUBLISH 报文,这就导致接收方将收到重复的消息。

重传 PUBLISH 报文的时候,PUBLISH 中的 DUP 标志会被设置为 1,用以表示这是一个重传的报文。

但是作为接收方,它不能确定自己接收过这个消息,会把它当成一个新的消息来做处理。

因为,对于接收方来说,

  • 第一种情况,发送方由于没有收到 PUBACK 报文而重传了 PUBLISH 报文。但是接收方可能接收过这条消息了;
  • 第二种情况:发送方发的报文丢了,只能重传报文,但这个对于接收方来说是一个全新的消息

所以,可能会因为网络延迟等问题,造成重复消息消费。

2.4 QOS 2原理-只交付一次

2.4.1 原理介绍

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 报文时,也会将它视为一个全新的消息。

2.4.2 消息不丢失原因

消息不丢失原因:与 QoS 1 相同

消息不会重复原因:

快速回顾一下 QoS 1 消息无法避免重复的原因:

当我们使用 QoS 1 消息时,对接收方来说,回复完 PUBACK 这个响应报文以后 Packet ID 就重新可用了,也不管响应是否确实已经到达了发送方。所以就无法得知之后到达的,携带了相同 Packet ID 的 PUBLISH 报文,到底是发送方因为没有收到响应而重传的,还是发送方因为收到了响应所以重新使用了这个 Packet ID 发

送了一个全新的消息。

所以,消息去重的关键就在于,通信双方如何正确地同步释放 Packet ID,换句话说,不管发送方是重传消息还是发布新消息,一定是和对端达成共识了的。而 QoS 2 中增加的 PUBREL 流程,正是提供了帮助通信双方协商 Packet ID 何时可以重用的能力。

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

因此,对于接收方来说,能够以 PUBREL 报文为界限,凡是在 PUBREL 报文之前到达的 PUBLISH 报文,都必然是重复的消息;而凡是在 PUBREL 报文之后到达的 PUBLISH 报文,都必然是全新的消息。一旦有了这个前提,我们就能够在协议层面完成 QoS 2 消息的去重。

2.5 不同 QoS 的适用场景

2.5.1 Qos 0

QoS 0 的缺点是可能会丢失消息,消息丢失的频率依赖于你所处的网络环境,并且可能使你错过断开连接期间的消息,不过优点是投递的效率较高。

所以我们通常选择使用 QoS 0 传输一些高频且不那么重要的数据,比如传感器数据,周期性更新,即使遗漏几个周期的数据也可以接受。

2.5.2 Qos 1

QoS 1 可以保证消息到达,所以适合传输一些较为重要的数据,比如下达关键指令、更新重要的有实时性要求的状态等。但因为 QoS 1 还可能会导致消息重复,所以当

我们选择使用 QoS 1 时,还需要能够处理消息的重复,或者能够允许消息的重复。

消息重复带来的危害:

如果我们不对 QoS 1 进行去重处理,我们可能会遭遇这种情况,发布方以 1、2 的顺序发布消息,但最终订阅方接收到的消息顺序可能是 1、2、1、2。如果 1 表示开

灯指令,2 表示关灯指令,我想大部分用户都不会接受自己仅仅进行了开灯然后关灯的操作,结果灯在开和关的状态来回变化。

2.5.3 Qos 2

QoS 2 既可以保证消息到达,也可以保证消息不会重复,但传输成本最高。如果我们不愿意自行实现去重方案,并且能够接受 QoS 2 带来的额外开销,那么 QoS 2 将是

一个合适的选择。通常我们会在金融、航空等行业场景下会更多地见到 QoS 2 的使用。

3、消息详解

3.1 主题详解

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

iot/ocloud/device/c9829f034985ad97/state/notify
iot/ocloud/device/c9829f034985ad97/point/notify
iot/ocloud/device/+/point/notify
iot/ocloud/device/#

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

Mqtt 主题无需提前创建,无需自己删除,不用关心,随开随用。

3.1.1 主题通配符

MQTT 主题通配符包含单层通配符 + 及多层通配符 # ,主要用于客户端一次订阅多个主题。

单层通配符

  • 号是用于单个主题层级匹配的通配符,在使用单层通配符时,单层通配符必须占据整个层级,例如:
+ 有效
test/+ 有效
test/+/temperature 有效
test+ 无效(没有占据整个层级)

如果客户端订阅了主题iot/ocloud/device/+/point/notify 则中间的+号可以随机,都能收到

iot/ocloud/device/1/point/notify
iot/ocloud/device/2/point/notify
iot/ocloud/device/3/point/notify

3.1.2 多层通配符

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

例如

# 有效,匹配所有主题
test/# 有效
test/bedroom# 无效(没有占据整个层级)
test/#/temperature 无效(不是主题最后一个字符)

如果客户端订阅主题 test/# ,则以test后面层级的消息都能收到

test
test/temperature
test/1/temperature

3.1.3 系统主题

SYS/开头的主题为系统主题,系统主题主要用于获取MQTT服务器自身状态、消息统计、客户端上下线事件等数据。目前,MQTT协议暂未明确规定SYS/ 开头的主题为系统主题,系统主题主要用于获取 MQTT 服务器自身状态、消息统计、客户端上下线事件等数据。目前,MQTT 协议暂未明确规定 SYS/

主题标准,但大多数 MQTT 服务器都遵循该标准建议

例如,EMQX 服务器支持通过以下主题获取集群状态:

主题说明
$SYS/brokersEMQX 集群节点列表
$SYS/brokers/emqx@127.0.0.1/versionEMQX 版本
$SYS/brokers/emqx@127.0.0.1/uptimeEMQX 运行时间
$SYS/brokers/emqx@127.0.0.1/datetimeEMQX 系统时间

EMQX 还支持客户端上下线事件,收发流量、消息收发、系统监控等丰富的系统主题,用户可通过订阅$SYS/#主题获取所有系统主题消息。

比如订阅客户端上下线事件主题:

$SYS/brokers/emqx@172.17.0.4/clients/+/connected       # 订阅客户端上线的主题
$SYS/brokers/emqx@172.17.0.4/clients/+/disconnected    # 订阅客户端下线的主题

注意:监听系统主题需要在 broker 端开通对应的访问权限

3.2 会话介绍

MQTT 客户端和 MQTT 服务器之间的连接被称为会话。

每个 MQTT 客户端都可以启动一个或多个会话,通过会话可以实现客户端和服务器之间的消息传递。

3.2.1 常见的配置参数

  • Clean start

    • 作用:用于指示客户端在和服务器建立连接的时候应该尝试恢复之前的会话还是直接创建全新的会话

    • 常见取值以及含义:

      • 0:服务端存在一个关联此客户端标识符(Client ID)的会话,服务端必须基于此会话的状态恢复与客户端的通信(之前的订阅信息会再次绑定,并且会接收到客户端断开时,发布者所发布的消息),如果不存在任何关联此客户端标识符的会话,服务端必须创建一个新的会话。
      • 1:客户端和服务端必须丢弃任何已存在的会话,并开始一个新的会话。
  • Session expiry interval

    • 决定了会话状态数据在服务端的存储时长。

    • 常见取值:

      • 没有指定此属性或者设置为 0,表示会话将在网络连接断开时立即结束
      • 设置为一个大于 0 的值,则表示会话将在网络连接断开的多少秒之后过期
      • 设置为 0xFFFFFFFF,即 Session expiry interval 属性能够设置的最大值时,表示会话数据永不过期。
    • 服务端使用 client ID 来唯一的标识每个会话,如果客户端想要在连接时复用之前的会话,那么必须使用与此前一致的 client id

3.2.2 会话演示

具体步骤:

1、在MQTTX中设置关闭自动重订阅功能

2、创建一个名为 sub 的客户端连接,将 MQTT Version 设置为 5.0,开启 Clean Start,Session Expiry Interval 设置为 300 秒,然后链接到MQTT的服务端,并订阅主题 mqttx_290c747e/test

3、创建一个名为 pub 的客户端连接向主题 mqttx_290c747e/test 发布消息,消息内容可以随意设置,我们将看到 sub 客户端收到这些消息。这时我们断开 sub 客户端的连接,然后继续通过 pub 客户端发布消息:

4、接下来,我们将 sub 客户端的 Clean Start 选项关闭,并保持 Session Expiry Interval 为 300 秒,然后再次连接。我们将看到 sub 客户端陆续收到我们在它离线期间发布的消息:

以上就是 MQTT 会话为离线客户端缓存消息的能力

3.3 消息详解

3.3.1 保留消息

普通消息:普通消息在发送之前其所对应的主题如果不存在订阅者,普通消息 MQTT 服务器会直接将其丢弃。

保留消息:保留消息可以保留在 MQTT 服务器中。任何新的订阅者订阅与该保留消息中的主题匹配的主题时,都会立即接收到该消息,即使这个消息是在它们订阅主题之前发布的。

如下图,当客户端订阅主题时,如果服务端存在该主题匹配的保留消息,则该保留消息将被立即发送给客户端。

常见使用场景:

  • 1、智能家居设备的状态只有在变更时才会上报,但是控制端需要在上线后就能获取到设备的状态;

  • 2、传感器上报数据的间隔太长,但是订阅者需要在订阅后立即获取到最新的数据;

  • 3、传感器的版本号、序列号等不会经常变更的属性,可在上线后发布一条保留消息告知后续的所有订阅者。

如何使用呢?

1、发布保留消息

在发布消息的时候将Retained 标记被设置为 true,则该消息即是 MQTT 中的保留消息(Retained Message)。发送完后,订阅者上线后立即推送这个状态;

注意事项:

  • 1、可以通过 Dashboard 查看保留消息
  • 2、MQTT 服务器会为每个主题存储最新一条保留消息
  • 3、在保留消息发布前订阅主题,将不会收到保留消息,需要待保留消息发布后,重新订阅该主题,才会收到保留消息。(就是当前收不到,必须重新上线后才能收到,个人感觉挺坑的)

保留消息的存储方式:

  • 内存存储(默认存储类型)、磁盘存储;

保留消息虽然存储在服务端中,但它并不属于会话的一部分,即使会话结束,保留消息也不会删除。

那如何删除保留消息呢?

1、客户端往某个主题发送一个 payload 为空的保留消息,服务端会删除这个主题下的保留消息;

2、在 MQTT 服务器上删除,比如 EMQX\MQTT 服务器提供了在 DashBoard 上删除保留消息的功能;

3、MQTT5.0 新增了消息过期间隔属性,发布时可使用该属性设置消息的过期事件,将会在过期事件后自动删除。

3.3.2 消息过期间隔

MQTT 可以通过 Session Expiry Interval 为离线客户端缓存尚未发送的消息,然后在客户端恢复连接时发送,但如果客户端离线时间较长,可能有一些寿命较短的消息已经没有必要发送给客户端了。

消息过期间隔是 MQTT 5.0 引入的一个新特性,它允许发布端为有时效性的消息设置一个过期间隔,如果该消息在服务端中停留超过了这个指定的间隔,那么服务端将不会再将它分发给订阅端。默认情况下,消息中不会包含消息过期间隔,这表示该消息永远不会过期。

注意:如果客户端在发布消息时设置了过期间隔,那么服务端在转发这个消息时也会包含过期间隔,但过期间隔的值会被更新为服务端接收到的值减去该消息在服务端停留的时间。这可以避免消息的时效性在传递的过程中丢失,特别是在桥接到另一个MQTT 服务器的时候

3.3.3 遗嘱消息

在 MQTT中,客户端可以在连接时在服务端中注册一个遗嘱消息,与普通消息类似,我们可以设置遗嘱消息的主题、有效载荷等等。当该客户端意外断开连接,服务端就会向其他订阅了相应主题的客户端发送此遗嘱消息。这些接收者也因此可以及时地采取行动,例如向用户发送通知、切换备用设备等等。

作用:借助于遗嘱消息可以感知到客户端是意外断开

实现原理

遗嘱消息在客户端发起连接时指定,它和 Client ID 、Clean Start 这些字段一起包含在客户端发送的 CONNECT 报文中。

与普通消息一样,我们可以为遗嘱消息设置主题(Will Topic)、保留消息标识位(will retain)、属性(will properties)、Qos(will Qos)和有效载荷。

这些字段的用法与它们在普通消息中时完全相同,只是遗嘱消息可用的属性与普通应用消息略有不同,下表列出了它们的具体区别:

遗嘱消息只是多了一个专属属性:will Delay Interval。

Will Delay Interval

作用:这个属性决定了服务端将在网络连接关闭后延迟多久发布遗嘱消息,并以秒为单位。

默认情况下,服务端总是在网络连接意外关闭时立即发布遗嘱消息。

但是很多时候,网络连接的中断是短暂的,所以客户端往往额能够重新连接并继续之前的会话。这就导致遗嘱消息可能被频繁的无意义的发送,如果没有指定或者将其设置为0,服务端将仍然在网络连接关闭时立即发布遗嘱消息。

但如果将 will Delay Interval 设置为一个大于 0 的值,并且客户端能够在 will Delay Interval 到期前恢复连接,那么该遗嘱消息将不会被发布。

遗嘱消息与会话

遗嘱消息是会话状态的一部分,当会话结束,遗嘱消息也无法继续单独存在,但是在遗嘱消息延迟发布期间,会话可能过期,也可能因为客户端在新的连接中设置 Clean Start 为 1,所以服务端需要丢弃之前的会话,为了避免丢失遗嘱,此时服务端必须发布遗嘱消息,即便 Will Delay Interval 还没有到期,所以服务端最终何时发布遗嘱消息,取决于 Will Delay Interval 到期和会话结束这两种情况谁先发生。

3.3.4 延迟发布

简介:MQTT 服务端收到发布者发布的消息以后,延迟一段时间以后再把消息转发给订阅者。

延迟发布的使用场景:

  • 农业智能化管理:在智能农业中,可能需要在特定时间启动灌溉系统或调节温室环境。通过 MQTT 延迟发布,可以预先设定好指令发布时间,如清晨自动发送;

无需人工干预,简化运维流程。

延迟发布主题格式: $delayed/{DelayInterval}/{TopicName}

  • delayed:使用delayed: 使用 `delay` 作为主题前缀的消息都将被视为需要延迟发布的消息
  • DelayInterval: 延迟发布的时间间隔,单位为秒,允许的最大间隔是 4294967 秒。如果 {DelayInterval} 无法被解析为一个整型数字,EMQX 将丢弃该消息,客户端不会收到任何信息。

举例子:

 $delayed/15/x/y:15 秒后将 MQTT 消息发布到主题 x/y。
 $delayed/60/a/b:1 分钟后将 MQTT 消息发布到 a/b。

具体步骤如下所示:

1、通过 Dashboard 配置延迟发布

打开 EMQX Dashboard,在左侧导航菜单中,点击管理-> 延迟发布

延迟发布页面,您可以进行以下配置:

  • 启用: 启用或禁用延迟发布。默认情况下,已启用。
  • 最大延迟消息数:可以指定延迟消息的最大数量。

3.4 订阅详解

3.4.1 订阅选项

订阅的组成:

1、主题过滤器:决定了服务端将向我们转发哪些主题下的消息;

2、订阅选项:是允许我们进一步定制服务端的转发行为;

MQTT 5.0提供了4个订阅选项:QoS、No Local、Retain As Published、Retain Handling

  • Qos:是最常用的一个订阅选项,它表示服务端在向订阅端发送消息时可以使用的最大Qos等级

  • No Local : 避免进入循环转发的情况,两个服务端互相订阅数据发送,给其中一个设置1即可

    • 0:默认值:服务端可以将消息转发给发布这个消息的客户端
    • 1:服务端不可以将消息转发给发布这个消息的客户端
  • Retain As Published:保留标记是否清除

    • 0(默认值):服务端在向此订阅转发应用消息时需要清除消息中的 Retain 标识不变
    • 1:服务端在向此订阅转发应用消息时需要保持消息中的 Retain 标识不变
  • Retain Handling:订阅建立时,是否需要发送保留消息

    • 0(默认值):表示只要订阅建立,就发送保留消息;

    • 1:表示只有建立全新的订阅而不是重复订阅时,才发送保留消息;

    • 2:表示订阅建立时不要发送保留消息;

3.4.2 共享订阅

在普通订阅时,我们每发布一条消息,所有匹配的订阅端都会收到这个消息的副本,当某个订阅端的消费速度无法跟上消息的生产速度时,就会出现性能瓶颈。

MQTT 5.0 引入了共享订阅特性,它使得 MQTT 服务端可以在使用特定订阅的客户端之间均衡地分配消息负载。这表示,当我们有两个客户端共享一个订阅时,那么每个匹配该订阅的消息都只会有一个副本投递给其中一个客户端。

共享订阅不仅为消费端带来了极佳的水平扩展能力,使我们可以应对更高的吞吐量,还为其带来了高可用性,即使共享订阅组中的一个客户端断开连接或发生故障,其他客户端仍然可以继续处理消息,在必要时还可以接管原先流向该客户端的消息流。

共享订阅的分类?

1、启用共享订阅:为一组订阅者的原始主题添加指定前缀

2、共享订阅分类

前缀格式示例前缀真实主题名
带群组格式$share/abc/t/1$share/abct/1
不带群组格式$queue/t/1$queue/t/1

带群组的共享订阅

我们可以在原始主题前加上 $share/ 的前缀为分组的订阅者启用共享订阅。

Emqx 同时将消息转发给不同的组,属于同一组的订阅者实现负载均衡消费消息。

这个还是非常实用的

准备四个订阅者。

  • 1 订阅 $share/group1/xiaolei
  • 2 订阅 $share/group1/xiaolei
  • 3 订阅 $share/group2/xiaolei
  • 4 订阅 xiaolei

测试:往 xiaolei 直接发送msg

group1 中只有一个订阅者能收到消息,其他订阅者都能收到。

不带群组的共享订阅:

$queue/ 为前缀的共享订阅是不带群组的共享订阅。它是 $share 订阅的一种特例。您可以将其理解为所有订阅者都在一个订阅组中:

拓展:可以去 DashBoard 进行负载均衡算法的配置

大致可以分为:

1、随机(Random),在共享订阅组内随机选择一个会话发送消息。

2、轮询(Round Robin),在共享订阅组内按顺序选择一个会话发送消息,循环往复。

3、哈希(Hash),基于某个字段的哈希结果来分配。

4、粘性(Sticky),在共享订阅组内随机选择一个会话发送消息,此后保持这一选择,直到该会话结束再重复这一过程。

6、本地优先(Local),随机选择,但优先选择与消息的发布者处于同一节点的会话,如果不存在这样的会话,则退化为普通的随机策略。

3.4.3 排它订阅

排它订阅允许对主题进行互斥订阅,一个主题同一时刻仅被允许存储在一个订阅者,在当前订阅者未取消订阅前,其他订阅者都将无法订阅对应主题。

要进行排它订阅,您需要为主题名称添加 exclusive/前缀,如exclusive/ 前缀,如 exclusive/xiaolei/iot

当某个客户端 A 订阅 exclusive/t/1后,其他客户端再订阅exclusive/t/1 后,其他客户端再订阅 exclusive/t/1 就会失败,直到 A 取消了这个订阅。

注意: 排它订阅必须使用 $exclusive/ 前缀,在上面的示例中,其他客户端依然可以通过 t/1 成功进行订阅。

3.4.4 自动订阅

自动订阅能够给 EMQX 设置多个规则,在设备成功连接后按照规则为其订阅指定主题,不需要额外发起订阅。

配置自动订阅规则:【管理---mqtt高级特性---自动订阅---添加】

4、EMQX Dashboard

4.1 访问控制

目前 EMQX 提供了三种认证方式,包含有:

  1. Password-Based,使用客户端 ID 或用户名加密码的认证方式;

  2. JWT,客户端可以在用户名或密码字段中携带 JWT token 来进行认证;

  3. SCRAM,MQTT 5.0 中的增强认证,可以实现对客户端和服务器的双向认证。

当选择 Password-Based 的认证方式后,用户可以选择存储认证数据的数据库或提供认证数据功能的 HTTP 服务器,数据库包含两类:

  • EMQX 的内置数据库,即选择 Built-in Database
  • 外部数据库,支持选择并连接到一些主流数据库,包括:MySQLPostgreSQLMongoDBRedis 等。

除数据库外,还可以直接使用能够提供认证数据的 HTTP 服务,即选择 HTTP Server

4.2 用户管理

对于使用内置数据库的用户来说,在认证列表页点击 用户管理,可以来到用户管理页面,在该页面,可以管理认证信息,例如添加或删除用户名和密码,也可以通过下载

模版,在模版内填充好相关的认证信息,点击 导入 来批量创建认证相关的用户信息。

如果是mysql,则可以写入sql加密方式插入。

集群的话,得用外部数据库

4.3 数据集成

EMQX 的数据集成功能不单单可以快速的将物联网设备产生的数据传递到业务系统中,也可以和其他的外部数据库系统进行集成,实现数据的快速传输。

sink和source组件

数据集成使用 SinkSource 组件与外部数据系统对接。

1、Sink 用于将消息发送到外部数据系统,例如 MySQL、Kafka 或 HTTP 服务等。

2、Source 则用于从外部数据系统接收消息,例如 MQTT、Kafka 或 GCP PubSub。

连接器

连接器负责与外部数据系统的连接,用户可以为不同的外部数据系统创建不同的连接器,一个连接器可以为多个 Sink/Source 提供连接。

规则引擎

规则引擎是 EMQX 内置基于 SQL 的数据处理组件,搭配数据集成无需编写代码即可实现一站式的 IoT 数据提取、过滤、转换、存储与处理,以加速应用集成和业务创新。

规则的组成:规则描述了 数据来源数据处理过程处理结果去向 三个方面:

1、数据来源:规则的数据源可以是消息或事件,也可以是外部的数据系统 (source)。规则通过 SQL 的 FROM 子句指定数据的来源;

2、数据处理过程:规则通过 SQL 语句和函数来描述数据的处理过程。SQL 的 WHERE 子句用于过滤数据,SELECT 子句以及 SQL 函数用于提取和转换数据;

3、处理结果去向:规则可以定义一个或多个动作来处理 SQL 的输出结果。如果 SQL 执行通过,规则将按顺序执行相应的动作,比如将处理结果存储到数据库、或者重新

发布到另一个 MQTT 主题等。支持的动作如下:

  • 消息重发布:将结果发布到指定 MQTT 主题

  • 控制台输出:将结果输出到控制台或日志中

  • 发送到各类 Sink:将结果发送到外部数据系统中,如 MQTT 服务,Kafka,PostgreSQL 等

Webhook 提供一种将 EMQX 客户端消息和事件集成到外部 HTTP 服务器的方法

Webhook 是EMQX 开箱即用的功能,当客户端向特定主题发布消息,或执行特定操作时就会触发 Webhook,将事件数据和消息数据转发到预设的 http 服务器中。