如果不考虑持久会话的因素,那么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的订阅选项
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中,客户端的意外断开可以分为以下几种情况
手机扫一扫
移动阅读更方便
你可能感兴趣的文章