MQTT 订阅选项的使用

news2024/12/26 21:17:10

在 MQTT 发布/订阅模式介绍这篇博客中,我们已经了解到,我们需要先向服务端发起订阅,才能从服务端接收对应的消息。如果说订阅时指定的主题过滤器决定了服务端将向我们转发哪些主题下的消息,那么订阅选项则是允许我们进一步定制服务端的转发行为。

在本文中,我们将重点介绍在 MQTT 中哪些订阅选项可供我们使用,以及它们的使用方法。

订阅选项

在 MQTT 中,一个订阅由一个主题过滤器和对应的订阅选项组成。所以理论上,我们可以为每个订阅都设置不同的订阅选项。

MQTT 5.0 提供了 4 个订阅选项,分别是 QoS、No Local、Retain As Published、Retain Handling,而 MQTT 3.1.1 则仅提供了 QoS 这一个订阅选项。不过这些 MQTT 5.0 新增的订阅选项的默认行为,仍与 MQTT 3.1.1 保持一致,如果你正准备从 MQTT 3.1.1 升级到 MQTT 5.0,这会非常地友好。

现在,让我们一起看看这些订阅选项的作用吧。

QoS

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

客户端可能会在订阅时指定一个小于 2 的 QoS,因为它的实现不支持 QoS 1 或者 QoS 2。而如果服务端支持的最大 QoS 小于客户端订阅时请求的最大 QoS,那么显然服务端将无法满足客户端的要求,这时服务端就会通过订阅的响应报文(SUBACK)告知订阅端最终授予的最大 QoS 等级,订阅端可以自行评估是否接受并继续通信。

image.png

一个简单的计算公式:

服务端最终授予的最大 QoS = min ( 服务端支持的最大 QoS, 客户端请求的最大 QoS )

但是,我们在订阅时请求的最大 QoS,并不能限制发布端发布消息时使用的 QoS。当我们订阅时请求的最大 QoS,小于消息发布时的 QoS 时,为了尽可能地投递消息,服务端不会忽略这些消息,而是会在转发时对这些消息的 QoS 进行降级处理。

image.png

同样,我们也有一个简单的计算公式:

消息被转发时的 QoS = min ( 消息原始的 QoS, 服务端最终授予的最大 QoS )

No Local

No Local 只有 0 和 1 两个可取值,为 1 表示服务端不能将消息转发给发布这个消息的客户端,为 0 则相反。

这个选项通常被用在桥接场景中。桥接本质上是两个 MQTT Server 建立了一个 MQTT 连接,然后相互订阅一些主题,Server 将客户端的消息转发给另一个 Server,而另一个 Server 则可以将消息继续转发给它的客户端。

image.png

那么最简单的一个例子,我们假设两个 MQTT Server 分别是 Server A 和 Server B,它们分别向对方订阅了 # 主题。现在,Server A 将一些来自客户端的消息转发给了 Server B,而当 Server B 查找匹配的订阅时,Server A 也会位于其中。如果 Server B 将消息转发给了 Server A,那么同样 Server A 在收到消息后又会把它们再次转发给 Server B,这样就陷入了无休止的转发风暴。

而如果 Server A 和 Server B 在订阅 # 主题的同时,将 No Local 选项设置为 1,就可以完美地避免这个问题。

Retain As Published

Retain As Published 同样只有 0 和 1 两个可取值,为 1 表示服务端在向此订阅转发应用消息时需要保持消息中的 Retain 标识不变,为 0 则表示必须清除。

Retain As Published 与 No Local 一样,同样也是主要适用于桥接场景。我们知道当服务端收到一条保留消息时,除了将它存储起来,还会将它像普通消息一样转发给当前已经存在的订阅者,并且在转发时会清除消息的 Retain 标识。

这在桥接场景下带来了一些问题。我们继续沿用前面的设定,当 Server A 将保留消息转发给 Server B 时,由于消息中的 Retain 标识已经被清除,Server B 将不会知道这原本是一条保留消息,自然不会再存储它。这就导致了保留消息无法跨桥接使用。

那么在 MQTT 5.0 中,我们可以让桥接的服务端在订阅时将 Retain As Published 选项设置为 1,来解决这个问题。

image.png

Retain Handling

Retain Handling 这个订阅选项被用来向服务端指示当订阅建立时,是否需要发送保留消息。

我们知道默认情况下,只要订阅建立,那么服务端中与订阅匹配的保留消息就会下发。

但某些时候,客户端可能并不想接收保留消息,比如客户端在连接时复用了会话,但是客户端无法确认上一次连接中是否成功创建了订阅,所以它可能会再次发起订阅。如果订阅已经存在,那么可能保留消息已经被消费过了,也可能服务端已经在会话中缓存了一些离线期间到达的消息,这时客户端可能并不希望服务端发布保留消息。

另外,客户端也可能在任何时刻都不想收到保留消息,即使是第一次订阅。比如我们将开关状态作为保留消息发送,但对某个订阅端来说,开关事件将触发一些操作,那么在这种情况下不发送保留消息是很有用的。

这三种不同的行为,我们可以通过 Retain Handling 来选择。

  • 将 Retain Handling 设置为 0,表示只要订阅建立,就发送保留消息;

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

  • 将 Retain Handling 设置为 2,表示订阅建立时不要发送保留消息。

演示

订阅选项 QoS 的演示

  1. 在 Web 浏览器上访问 MQTTX Web。

  2. 创建一个使用 WebSocket 的 MQTT 连接,并且连接免费的 公共 MQTT 服务器:

    MQTTX

  3. 连接成功后,我们订阅主题 mqttx_4299c767/demo,并指定 QoS 为 0。由于公共服务器可能同时被很多人使用,为了避免主题与别人重复,我们可以将 Client ID 作为主题前缀:

    Subscribe to the topic "mqttx_4299c767/demo"

  4. 订阅成功后,我们向主题 mqttx_4299c767/demo 发布一条 QoS 1 消息,这时我们将看到,我们发出的是 QoS 1 消息,但收到的却是 QoS 0 消息,这说明发生了 QoS 降级:

    Publish a QoS 1 message

订阅选项 No Local 的演示

  1. 在 Web 浏览器上访问 MQTTX Web。

  2. 创建一个使用 WebSocket 的 MQTT 连接,并且连接免费的公共 MQTT 服务器。

  3. 连接成功后,我们订阅主题 mqttx_4299c767/demo,并且将 No Local 设置为 true:

    Subscribe to the topic "mqttx_4299c767/demo"

  4. 订阅成功后,与前面 QoS 的演示一样,我们还是由订阅端自己来发布消息,但这一次我们会发现订阅端将无法收到消息:

    Publish MQTT Message

订阅选项 Retain As Published 的演示

  1. 在 Web 浏览器上访问 MQTTX Web。

  2. 创建一个使用 WebSocket 的 MQTT 连接,并且连接免费的公共 MQTT 服务器。

  3. 连接成功后,我们先订阅主题 mqttx_4299c767/rap0,并且将 Retain As Published 设置为 false,然后订阅主题 mqttx_4299c767/rap1,并且将 Retain As Published 设置 true:

    Subscribe to the topic "mqttx_4299c767/rap0"

    Subscribe to the topic "mqttx_4299c767/rap1"

  4. 订阅成功后,我们分别向主题 mqttx_4299c767/rap0mqttx_4299c767/rap1 发布一条保留消息,我们将看到前者收到的消息中 Retain 标识被清除,而后者收到的消息中 Retain 标识被保留:

    Receive messages

订阅选项 Retain Handling 的演示

  1. 在 Web 浏览器上访问 MQTTX Web。

  2. 创建一个使用 WebSocket 的 MQTT 连接,并且连接免费的公共 MQTT 服务器。

  3. 连接成功后,我们先向主题 mqttx_4299c767/rh 发布一条保留消息。然后订阅主题 mqttx_4299c767/rh,并且将 Retain Handling 设置为 0:

    Publish a retained message to the topic "mqttx_4299c767/rh"

  4. 订阅成功后,我们将收到服务端发送的保留消息:

    Receive the retained message

  5. 取消当前订阅,重新订阅主题 mqttx_4299c767/rh,并且将 Retain Handling 设置为 2。不过这一次订阅成功后,我们将不会收到服务端发送的保留消息:

    Retain Handling set to 2

在 MQTTX 中,我们没有办法演示 Retain Handling 设置为 1 时的效果。不过你可以在 这里 获取订阅选项的 Python 示例代码。

版权声明: 本文为 EMQ 原创,转载请注明出处。
原文链接:https://www.emqx.com/zh/blog/an-introduction-to-subscription-options-in-mqtt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/772231.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python学习(十三)

安装包的方法: #python数据和json数据的相互转换 import json #准备列表,列表的每一个元素都是字典,将其转换为JSON data [{"name":"大大","age":21},{"name":"小小","age":21…

Java工程师进阶:50小时,搞定企业级核心框架

哈喽,做Java开发的同学们注意啦!!! 小谷又来分享技术了,关乎Java工程师技能进阶与升职加薪的方向哦~~ 最近老有小伙伴私信小谷,说自己的Java水平已经到了瓶颈期,不知道该咋突破,长久…

STM32定时器中断的使用示例

STM32定时器中断的使用示例 前言硬件和软件cubemx使能定时器中断中断服务函数案例输出结果 前言 上一篇博客实现了定时器输出pwm,这篇接着上次的工程,在上次的工程上做简单的配置即可 硬件和软件 硬件使用的是stm32h750vbt6;软件用到了stm…

JAVA电商 B2B2C商城系统免费搭建 多用户商城系统 直播带货 新零售商城 o2o商城 电子商务 拼团商城 分销商城 手机商城免费搭建

1. 涉及平台 平台管理、商家端(PC端、手机端)、买家平台(H5/公众号、小程序、APP端(IOS/Android)、微服务平台(业务服务) 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框…

星火认知大模型,让我感受到了国产AI的崛起

文章目录 一、申请和测试代码二、实测GPT4.0和星火认知大模型的对比2.1 测试网站2.2 经典问题提问对比2.3 代码问题提问对比2.4 论文问题对比2.5 评价 一、申请和测试代码 在我之前的一篇文章中,我分享了如何申请星火认知大模型的内测,并提供了一份可以…

云曦期末复现

serialize 代码审计&#xff0c;给1传参&#xff0c;满足password的值为yunxi&#xff0c;那么反序列化前就会执行__wakeup函数&#xff0c;从而得到flag.php&#xff0c;但是password的值被定死为1&#xff0c;利用PHP反序列化的字符逃逸: <?php error_reporting(0); hig…

Ubuntu搭建docker+laradock

使用Ubuntu搭建dockerlaradock windows 下载Ubuntu工具二选一 链接&#xff1a;https://pan.baidu.com/s/154K6MKdFZxWqaTn2q-6MSQ 提取码&#xff1a;06lc https://www.jianshu.com/p/b7e11d0dbe8c借鉴地址&#xff1a;https://zhuanlan.zhihu.com/p/547169542 备注&#x…

GO语言semaphore信号量

一般地,我们唤醒在等待队列中的线程会使用系统调用和切换线程这样的开销比较大. 本质上.是结合自旋锁和调度器调度后的锁.不过这种机制适合线程不适合协程.因为调度器调度需要切换线程,而协成切换不能切换线程. 协程等待一个锁如何等待和唤醒呢? 在GO语言中,我们使用semaphor…

使用端点中心进行补丁管理

什么是补丁管理 补丁管理是为网络中的所有操作系统和应用程序检测、下载、测试、批准和安装新补丁/缺失补丁的过程。它需要集中查看网络中端点的适用补丁&#xff0c;以便可以一目了然地对易受攻击、高度易受攻击和健康的系统进行分类。这有助于发现需要注意的系统&#xff0c…

十分钟让你了解 Linux ABI

getline() 提供了一种更灵活的方法&#xff0c;可以在不破坏系统的情况下将用户数据读入程序。 在 C 语言中读取字符串是一件非常危险的事情。当读取用户输入时&#xff0c;程序员可能会尝试使用 C 标准库中的 gets 函数。它的用法非常简单&#xff1a; char *gets(char *stri…

socket编程代码示例

1. TCP server client模拟聊天对话框 server.c /* server.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include &l…

jmeter使用PerfMon插件监测服务器资源时 连接超时

异常提示&#xff1a; ERROR: java.net.ConnectException: Connection timed out: connect 解决方案&#xff1a; 一种情况下是端口未启用&#xff1b; 另一种情况更新端口号&#xff0c;将默认端口进行修改 4444 修改为其他未使用的端口号&#xff08;例如&#xff1a;4445 &…

vue基础-axios详解

1.1 背景 Axios 是一个基于 promise 的 HTTP 库&#xff0c;可以用在浏览器和 node.js 中, 也是 vue 官方推荐使用的 http 库&#xff1b;封装axios&#xff0c;一方面为了以后维护方便&#xff0c;另一方面也可以对请求进行自定义处理。 1.2 vue请求模块选择 2 应用 2.1 安…

第八十八回:创建一个调色板

文章目录 概念介绍实现方法整体思路具体步骤 示例代码 我们在上一章回中介绍了打印日志相关的内容&#xff0c;本章回中将介绍 如何创建一个调色板.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在本章回中介绍的调色板是一个具有各种颜色的窗口&#xff0c…

大口径均匀性大于98%积分球均匀光源

大口径宽视场光学成像遥感器的探测元件采用阵列探测器或多片阵列探测器拼接而成,由于阵列探测器像元间辐射响应差异和光学系统的非理想性&#xff0c;影响了光学成像遥感器的成像质量&#xff0c;进而影响测量的准确性&#xff0c;因此在有效应用前必须对大口径宽视场光学成像遥…

【hadoop】hadoop的体系架构

hadoop的体系架构 HDFS的体系架构NameNodeedits文件&#xff08;客户端的操作日志&#xff09;fsimage文件&#xff08;元信息文件&#xff09; DataNodeSecondary NameNode Yarn的体系架构HBase主从架构的单点故障的问题 HDFS的体系架构 NameNode NameNode&#xff1a;主节点…

idea-实现热部署

idea-实现热部署 今天在进行idea 开发时突然发现热部署失败了&#xff0c;每次修改内容都要去restart server一次 这样比较麻烦&#xff0c;故而总结一下idea实现热部署的方法&#xff1a; 步骤一&#xff1a; 选择edit configuration 然后跳出server 的配置&#xff0c;方框…

Go语言之并发编程练习

GO协程初识 package mainimport ("fmt""sync""time" )func read() {defer wg.Done()fmt.Println("read start")time.Sleep(time.Second * 3)fmt.Println("read end") }func listenMusci() {defer wg.Done()fmt.Println(&qu…

ENSP实验三:搭建VPN(GRE,未配置安全策略)

目录 1、基础配置&#xff1a;IP地址&#xff0b;防火墙安全区域&#xff1b; 2、使ISP&#xff08;运营商&#xff09;网络的路由可达-OSPF&#xff1b; 3、建立VPN隧道&#xff08;两个防火墙之间&#xff09;&#xff1b; 4、引流 1、基础配置&#xff1a;IP地址&#xf…

c语言小项目——通讯录中阶(动态内存版)

通讯录初阶&#xff1a;点这里 通讯录高阶&#xff1a;点这里 动态内存版改进之处结构体初始化通讯录添加联系人销毁通讯录 完整代码contact.hcontact.ctest.c 动态内存版改进之处 结构体 contact.c 初始化通讯录 contact.h contact.c 添加联系人 contact.c 销毁通讯录 te…