MQTT-保留消息和遗嘱消息
保留消息
为什么需要保留消息
如果不考虑持久会话的因素,那么MQTT订阅只有在客户端连接之后才能创建,所以服务端不能提前预知某个主题会被哪些服务端订阅或者某个客户端会订阅哪些主题,所以当消息到达服务端之后,服务端只会把消息分发给当前已经存在的订阅者,分发完成消息就会从服务端中删除,如果当前没有任何订阅者,消息就会立即丢弃,如果订阅端在在这之后上线的,就会错过这个消息,为了解决该场景的问题,MQTT提供了保留消息
如何使用保留消息
我们可以在发送PUBLISH的时候把Retain设置为1或者True,表示当前消息是保留消息,保留消息进入服务端,会像普通消息一样转发给当前的订阅者,还会被保留在MQTT的服务端中,每当有新的订阅建立,MQTT服务端都会检索是否存在与这个订阅匹配的保留消息,然后把匹配的保留消息下发给订阅者,由于订阅的时候可以使用主题通配符,所以可能匹配到多个保留消息,这些消息将依次下发给订阅者
保留消息的更新
通过保留消息,我们可以使订阅者上线后立即获得数据更新,不必等待新一次的消息,每个主题最多可以储存一条保留消息,如果主题下的保留消息已经存在,那么新到达的保留消息就会替换原来的保留消息,保留消息会一直储存在服务端中,由于他不属于会话状态的一部分,所以即便发布端会话过期,也不会影响保留消息存储,如果想要清空这个主题的保留消息,可以通过发送一个payload为空的保留消息来实现,这个为空的保留消息会合其他保留消息一样转发给订阅者,区别是在于不会被服务端存储,使用这种方式的话,我们需要确保订阅端不会把为空的消息视为一个错误
需要注意的是,QoS 0 可能丢消息的特性,可能会导致保留消息删除不成功,QoS 1 可能重复到达的特性,保留消息又可能多次删除,如果不希望出现删除失败或者多次删除的情况,可以使用QoS 2 来发布 payload为空的保留消息
保留消息的过期时间
如果担心一条保留消息失去了时效性,还长时间保留在服务端中导致被后来的订阅者消费的话,MQTT 5.0版本可以为保留消息设置过期时间,PUBLISH的 Message Expiry Interval 属性,单位是秒
保留消息的发送机制
默认情况下,当保留消息当成普通消息向订阅者转发的时候,保留消息中的retain标识会被清除也就是设置为0,只有当新的订阅建立的时候,发送保留消息的retain会设置为1,表示这是一个保留消息
针对上述情况多个服务端桥接的时候,会衍生一个问题,比如服务端A向服务端B订阅了主题,当服务端B收到一个保留消息向服务端A转发的时候清除retain标识,导致服务端A收到该保留消息后只会转发给订阅者,不会保存保留消息,在MQTT 5.0中,提供了 Retain As Published 的订阅选项,如果该选项设置为0,就是默认的逻辑,如果设置为1,那么保留消息当做普通消息转发的时候,Retain标识不会被清除,依然是1
还有一个选项会影响保留消息的行为,在某些场景下,虽然客户端复用了上一次的会话,但是无法确定上一次会话中是否成功订阅了某个主题,所以只能再次订阅,如果订阅已经存在,其实服务端已经给客户端缓存了离线期间的消息,这种情况下,客户端在重连后,其实并不需要获取保留消息,但是现在只要有订阅建立,订阅匹配就会下发保留消息,为了解决这个问题,在MQTT 5.0中,提供了 Retain Handling的订阅选项
- Retain Handling = 0,订阅建立的时候发送保留消息
- Retain Handling = 1,订阅建立时若该订阅当前不存在则发送保留消息
- Retain Handling = 2,订阅建立时不发送保留消息
保留消息的注意事项
- 在MQTT中,同一条普通消息只能被同一个客户端消费一次,保留消息可能会被重复消费,客户端进行订阅,服务端下发匹配的保留消息,即时这个消息之前已经下发过了,只要保留消息在客户端的两次订阅期间没有更新,客户端就会重复消费到同一条消息,如果客户端的订阅是在保留消息到达服务端之前建立的,消息转发后 客户端重新连接,没有更新过保留消息,就是重复收到了两条同样的消息
- 不能通过主动删除已经消费过的保留消息来避免重复,因为可能其他人也使用该主题下的保留消息,我们就不能去删除他,其次也没有办法正确判断当前服务器中的保留消息有没有被自己消费过,我们可以参考QoS 1 去重的做法,在保留消息的payload中增加一个时间戳,订阅者记录最后消费的消息的时间戳,和新到达的保留消息的时间戳进行比较,如果后者的时间戳更新,就是一个新的消息,反之就是一个重复的消息
- 我们可以通过保留消息减少消息的发布频次,对于一些固定周期、状态等以确保新上线的客户端尽快取得数据,有了保留消息,我们可以只在状态发生变更时进行发布
遗嘱消息
为什么需要遗嘱消息
MQTT的订阅发布机制,解耦了消息的发送方和接收方,这使我们没有办法获取对端的状态,为了解决该问题,MQTT提供了遗嘱消息,为意外断线的客户端提供了对外发出通知的能力
如何使用遗嘱消息
使用遗嘱消息,客户端需要在连接时,也就是connect报文中指定遗嘱消息,除了正常CONNECT报文字段,需要为遗嘱消息提供以下字段
Will Topic #遗嘱消息主题
Will QoS # 遗嘱消息级别
Will Retain # 将遗嘱消息设置为保留消息
'''
遗嘱消息一旦发布,就会在服务端的会话状态中删除,不能多次消费,我们不能保证遗嘱消息发出的时候订阅端是否在线,为了避免错过遗嘱消息,可以使用Will Retain = True 字段将遗嘱消息设置为保留消息,这样订阅了该主题的客户端不管什么时候上线,都可以收到另外一方的离线通知
'''
Will Properties # 遗嘱消息属性 ↓
Will Delay Interval #遗嘱消息的属性↑, 设置遗嘱消息的延迟发送时间
'''
在MQTT 5.0中,可以使用 Will Delay Interval 设置延迟发送遗嘱消息,单位是S,如果客户端及时恢复,那么遗嘱消息的发生倒计时就会终止,可以避免客户端在短暂离线后恢复,可以继续服务时但是遗嘱消息已经发出的情况,和保留消息不同的是,遗嘱消息是会话状态的一部分,没有办法存在比会话更长的时间,如果遗嘱延迟时间大于会话过期时间,会话结束的时候遗嘱会立即发布
'''
Will Payload # 遗嘱消息内容
在客户端连接成功后,遗嘱消息就会存储在服务端中,一旦客户端连接异常断开,服务端就会把遗嘱消息发送给对应的订阅者,如果客户端是正常断开,遗嘱消息则不会发布,在MQTT中,客户端的意外断开可以分为以下几种情况
- 服务端检测到了一个I/O故障或者网络故障
- 客户端在心跳的时间内未能通讯
- 客户端在没有发生Reason Code 为0的DISCONNECT报文的情况下关闭了网络连接
- 服务端在没有收到DISCONNECT报文的情况下主动关闭了网络连接
- 在MQTT 3.1.1中,如果满足任意一个情况,服务端会在连接断开后立刻发布遗嘱消息,5.0中可以通过设置属性延迟发布
MQTT-保留消息和遗嘱消息的更多相关文章
- 数据采集,SCADA, 使用MQTT的方式来进行消息单/双向传输,什么场景使用MQTT
1.先来了解下: 看完得出关键字:发布.订阅模式,事件驱动,主题,生产与消费解耦 2.轻量级 普通的socket连接对服务器的消耗太大了,socket服务端是很消耗资源的,一台服务器能链接的客户端是有 ...
- 【笨嘴拙舌WINDOWS】键盘消息,鼠标消息
键盘消息 Windows系统无论何时只有一个窗口(可能是子窗口,也就是控件)能获得焦点. 焦点窗口通过windows消息来响应人的键盘操作,与键盘相关的常用消息罗列如下: WM_KEYDOWN 按 ...
- 微信公众号开发C#系列-6、消息管理-普通消息接受处理
1.概述 通过前面章节的学习,我们已经对微信的开发有了基本的掌握与熟悉,基本可以上手做复杂的应用了.本篇我们将详细讲解微信消息管理中普通消息的接收与处理.当普通微信用户向公众账号发消息时,微信服务器将 ...
- RabbitMQ 消息顺序、消息幂等、消息重复、消息事务、集群
1. 消息顺序 场景:比如下单操作,下单成功之后,会发布创建订单和扣减库存消息,但扣减库存消息执行会先于创建订单消息,也就说前者执行成功之后,才能执行后者. 不保证完全按照顺序消费,在 MQ 层面支持 ...
- redis 的消息订阅和消息队列的功能比较
消息队列常用的有 rabitMQ.kafka等.缓存服务器 redis 也可以做消息队列使用,他们的特点对比如下 消息协议: 消息队列支持包括AMQP,MQTT,Stomp等,并且支持 JMS 规范 ...
- 【python】-- RabbitMQ 队列消息持久化、消息公平分发
RabbitMQ 队列消息持久化 假如消息队列test里面还有消息等待消费者(consumers)去接收,但是这个时候服务器端宕机了,这个时候消息是否还在? 1.队列消息非持久化 服务端(produc ...
- SIP消息类型和消息格式
转自:http://blog.chinaunix.net/uid-1797566-id-2840904.html sip消息类型和消息格式 SIP是一个基于文本的协议,使用的是UTF-8字符集. SI ...
- 离奇失踪的WM_HOTKEY消息--浅析WIN32消息队列
故事的开端有些平淡,眼红于XXX小程序,认为写完该程序就有了和心仪的妹子多相处的机会,必须搞,必须酷,按钮不能有,界面得隐藏,这就想到了全局快捷键. 注册调用RegisterHotKey(m_hWnd ...
- RocketMQ源码详解 | Broker篇 · 其四:事务消息、批量消息、延迟消息
概述 在上文中,我们讨论了消费者对于消息拉取的实现,对于 RocketMQ 这个黑盒的心脏部分,我们顺着消息的发送流程已经将其剖析了大半部分.本章我们不妨乘胜追击,接着讨论各种不同的消息的原理与实现. ...
- C#开发微信门户及应用(3)--文本消息和图文消息的应答
微信应用如火如荼,很多公司都希望搭上信息快车,这个是一个商机,也是一个技术的方向,因此,有空研究下.学习下微信的相关开发,也就成为计划的安排事情之一了.本系列文章希望从一个循序渐进的角度上,全面介绍微 ...
随机推荐
- Blob文件下载type类型
let url = window.URL.createObjectURL(new Blob([文件流(一般为res.data)], {type: "Blob类型"}) let li ...
- Windows 10 输入法(仅桌面) %100 解决
大家好,今天我遇到了一件非常难受的一件事,那就是 WIndows 自带的输入发问题,无法输入中文!!! 这时我去网上找找解决方案,总结了一下几个: 文件检查 步骤 以管理员身份运行PowerShell ...
- A better jump —— 优化游戏中的跳跃
之前一提起角色的跳跃,想当然的想法就是:给角色一个向上的初速,然后由Unity的物理系统接管就好了嘛,这样忽略空气摩擦的影响,根据重力加速度,角色向上跳到最高点的时间和由最高点落下的时间相等,不是很合 ...
- vs2022和wsl2开发和调试c++代码(转载)
看见一个不错的帖子(知乎) https://zhuanlan.zhihu.com/p/390757559 里面最主要就是要保证wsl里面安装的东西够了,第二就是vs2022已经安装了linux的相关模 ...
- python之pyqt5-第一个pyqt5程序-图像压缩工具-小记
(如想转载,请联系博主或贴上本博地址) 此为学习pyqt5的第一个程序,图像压缩工具. 因为比较简单,下面直接贴上代码. 效果图如下: # -*- coding: utf-8 -*- import s ...
- element表格样式的修改
修改表格头部背景 .el-table th{ background: #f00; } 修改表格行背景 .el-table tr{ background: #f00; } 修改斑马线表格的背景 .el- ...
- beta冲刺:总结随笔
这个作业属于哪个课程 <班级的链接> 这个作业要求在哪里 <作业要求的链接> 这个作业的目标 beta冲刺总结 作业正文 .... 其他参考文献 ... 一.预期计划 | 6. ...
- 初探redis缓存击穿、穿透、雪崩问题
现分析Redis缓存使用过程失效的一些问题,在有缓存的情况下,查询数据的顺序是先查询缓存,如果查询到数据则直接返回数据,如果没有查询到数据,则到数据库中查询,数据库中有数据的话,将查询出的数据写到缓存 ...
- MySQL学习(二)事务的隔离级别
:规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的.较低级别的隔离通常可以执行更高的并发,系统的开销也更低 read uncommitted(未提交读):事务中的修改,即使没有 ...
- 集训第二周计划:把cf近期的div2除了最后一题给切完
太菜了太菜了,弄个训练计划. 晚上没事干的时候我想把博客园皮肤改一下,搜着搜着不知道怎么回事点进去一些竞赛选手的博客,比如这个 https://www.cnblogs.com/soda-ma/p/13 ...