在任何网络环境下,都会出现一方链接失败,好比离开公司大门那一刻没有了WIFI信号。但持续链接的另外一端-服务器可能不能当即知道对方已断开。相似网络异常状况,都有可能在消息发送的过程当中出现,消息发送出去,就丢失了。html
MQTT协议假定客户端和服务器端稳定状况通常,彼此之通讯管道不可靠,一旦客户端网络断开,状况就会很严重,很难恢复原状。java
但别忘记,不少客户端会有永久性存储设备支持,好比闪存ROM、存储卡等,在通讯出现异常的状况下能够用于保存关键数据或状态信息等。服务器
总之,异常网络状况很复杂,只能当心处理之。网络
在QoS > 0状况下,PUBLISH、PUBREL、SUBSCRIBE、UNSUBSCRIBE等类型消息在发送者发送完以后,须要等待一个响应消息,若在一个指定时间段内没有收到,发送者可能须要重试。重发的消息,要求DUP标记要设置为1.session
等待响应的超时应该在消息成功发送以后开始算起,而且等待超时应该是能够配置选项,以便在下一次重试的时候,适当加大。好比第一次重试超时10秒,下一次可能为20秒,再一次重试可能为60秒呢。固然,还要有一个重试次数限制的。.net
还 有一种状况,客户端从新链接,但未在可变头部中设置clean session标记,但双方(客户端和服务器端)都应该重试先前未发送的动态消息(in-flight messages)。客户端不被强制要求发送未被确认的消息,但服务器端就得须要重发那些未被去确认的消息。翻译
QoS level为Quality of Service level的缩写,翻译成中文,服务质量等级。code
MQTT 3.1协议在"4.1 Quality of Service levels and flows"章节中,仅仅讨论了客户端到服务器的发布流程,不太完整。由于决定消息到达率,可以提高发送质量的,应该是服务器发布PUBLISH消息到订阅者这一消息流方向。htm
至多发送一次,发送即丢弃。没有确认消息,也不知道对方是否收到。blog
Client
Server | ||
---|---|---|
QoS = 0 | PUBLISH ----------> |
Action: Publish message to subscribers then Forget Reception: <=1 |
针对的消息不重要,丢失也无所谓。
网络层面,传输压力小。
全部QoS level 1都要在可变头部中附加一个16位的消息ID。
SUBSCRIBE和UNSUBSCRIBE消息使用QoS level 1。
针对消息的发布,Qos level 1,意味着消息至少被传输一次。
发送者若在一段时间内接收不到PUBACK消息,发送者须要打开DUB标记为1,而后从新发送PUBLISH消息。所以会致使接收方可能会收到两次PUBLISH消息。针对客户端发布消息到服务器的消息流:
Client
Server | ||
---|---|---|
QoS = 1 DUP = 0 Message ID = x Action: Store message |
PUBLISH ----------> |
Actions:
Reception: >=1
|
Action: Discard message | PUBACK <---------- |
Message ID = x
|
针对服务器发布到订阅者的消息流:
Server
Subscriber | ||
---|---|---|
QoS = 1 DUP = 0 Message ID = x |
PUBLISH ----------> |
Actions:
Reception: >=1
|
PUBACK <---------- |
Message ID = x
|
发 布者(客户端/服务器)若因种种异常接收不到PUBACK消息,会再次从新发送PUBLISH消息,同时设置DUP标记为1。接收者以服务器为例,这可能 会致使服务器收到重复消息,按照流程,broker(服务器)发布消息到订阅者(会致使订阅者接收到重复消息),而后发送一条PUBACK确认消息到发布 者。
在业务层面,或许能够弥补MQTT协议的不足之处:重试的消息ID必定要一致接收方必定判断当前接收的消息ID是否已经接受过
但同样不可以彻底确保,消息必定到达了。
仅仅在PUBLISH类型消息中出现,要求在可变头部中要附加消息ID。
级别高,通讯压力稍大些,但确保了仅仅传输接收一次。
先看协议中流程图,Client -> Server方向,会有一个整体印象:
Client
Server | ||
---|---|---|
QoS = 2 DUP = 0 Message ID = x Action: Store message |
PUBLISH ----------> |
Action(a) Store message or Actions(b):
|
PUBREC <---------- |
Message ID = x | |
Message ID = x | PUBREL ----------> |
Actions(a):
or Action(b): Delete message ID |
Action: Discard message | PUBCOMP <---------- |
Message ID = x |
Server -> Subscriber:
Server
Subscriber | ||
---|---|---|
QoS = 2 DUP = 0 Message ID = x |
PUBLISH ----------> |
Action: Store message |
PUBREC <---------- |
Message ID = x | |
Message ID = x | PUBREL ----------> |
Actions:
|
PUBCOMP <---------- |
Message ID = x |
Server 端采起的方案a和b,都包含了什么时候消息有效,什么时候处理消息。两个方案二选一,Server端本身决定。但不管死采起哪种方式,都是在QoS level 2协议范畴下,不受影响。若一方没有接收到对应的确认消息,会从最近一次须要确认的消息重试,以便整个(QoS level 2)流程打通。
消息顺序会受许多因素的影响,但对于服务器程序,必须保证消息传递流程的每一个阶段要和开始的顺序一致。例如,在QoS level 2定义的消息流中,PUBREL流必须和PUBLISH流具备相同的顺序发送:
Client
Server | ||
---|---|---|
PUBLISH 1----------> PUBLISH 2 ----------> PUBLISH 3 ----------> |
||
PUBREC 1<---------- PUBREC 2 <---------- |
||
PUBREL 1----------> |
||
PUBREC 3<---------- |
||
PUBREL 2----------> |
||
PUBCOMP 1<---------- |
||
PUBREL 3----------> |
||
PUBCOMP 2<---------- PUBCOMP 3 <---------- |
流动消息(in-flight messages)数量容许有一个可保证的效果:
在MQTT协议中,PUBLISH消息固定头部RETAIN标记,只有为1才要求服务器须要持久保存此消息,除非新的PUBLISH覆盖。
对于持久的、最新一条PUBLISH消息,服务器不但要发送给当前的订阅者,而且新的订阅者(new subscriber,一样须要订阅了此消息对应的Topic name)会立刻获得推送。
Tip:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送,不是全部。
mqtt的消息流,以小和轻量为主要目标,QoS级别进行了消息传输的保证。