MQTT服务质量等级及抓包分析
什么是服务质量?
服务质量(QualityofService,QoS)等级是消息发送方与消息接收方之间的协议,对应着消息传递时不同的可靠程度。
MQTT有三种QoS等级:
- 至多一次(QoS 0)
- 至少一次(QoS 1)
- 只有一次(QoS 2)
MQTT中消息的发布和订阅都有QoS等级的设置,我们需要将其分开理解
1.publish的QoS等级
1.1 QoS 0
当client1 publish一条消息时,如果设置的QoS等于0,那么client1只会发送一次这条消息,就算broker没有收到,client1也不会管。对于broker来说,它收到client1 publish的消息,就会PUBLISH给众多客户端,收不到自然也不会PUBLISH。产生的效果:broker最终会PUBLISH 0条或1条消息,即至多一次。
1.2 QoS 1
上面那个例子,如果设置QoS等于1,那么client1发送一条消息,broker收到后会发给client1一个应答PUBACK,client1如果收到应答就会停止发送,否则,继续发送。broker做的事情也很简单,收到一次消息,就将消息PUBLISH给众多客户端,同时发现QoS等级为1,就向刚刚给自己publish消息的client1发送一个响应PUBACK。但是在这种交互中存在两个问题,1:client1 publish的消息有可能broker没能收到,2:broker回复的PUBACK client1没能收到。这样会造成:broker收到消息,将消息PUBLISH出去后,client1没能收到PUBACK,就会继续发送消息,broker就会继续PUBLISH,直到client1收到PUBACK为止。产生效果:broker最终会PUBLISH 至少1条消息,即至少一次。
1.3 QoS 2
还是上面的例子,如果设置QoS等于2,那么client1发送一条消息后,broker会回一个PUBREC(publish recevied)(但是这里注意broker虽然已经收到了消息但是它不会立马PUBLISH给众多客户端,还要等待后续步骤),client1收到PUBREC会回应一个PUBREL(publish release)(即既然你broker收到了,我client1就把这条消息释放了,保存起来太占地方),client1也不会再重复发这条消息了,因为都已经释放这个包了,broker收到PUBREL后会回复client1一个PUBCOMP(publish complete)(即这次消息传递完毕),broker发完PUBCOMP后,才会将刚才的消息PUBLISH给众多客户端。在PUBCOMP之前的交互中,如若有丢包同样会重传。产生效果:broker最终会PUBLISH 1条消息,不会多于1条,也不会少于1条。
应用场景:
为什么要有这三种服务质量等级呢?我们结合具体需求来思考一下
QoS 0 适合什么场景呢?适合那种消息很频繁但又不那么重要的应用,如每隔几分钟上报一次气温,数据丢了就丢了,反正待会还会上报,并且丢失一次数据也不会产生多大影响。
但是,如果消息对你很重要,你宁愿收到重复的也不愿意丢失一条,那你就采用QoS 1
但是,如果像支付类的应用也采用QoS 1的话,会产生重复支付的问题(其实已经支付成功了,只是手机没有收到回应,又再次发起支付),这时候就需要使用QoS 2了,它保证有且仅有一次消息成功传递。
2.subscribe的QoS等级
说完了publish的QoS,我们再来谈谈subscribe的QoS。其实我们完全可以使用上面三个例子的分析,因为clientA、clientB、clientC 订阅 broker的消息就相当于broker发布消息给他们(这点从后面的抓包分析也可得以验证),我们仍然可用publish的那一套去理解subscribe的QoS,即client A subscribe消息时设置的QoS等于0,那么broker最多只会发一次消息给clientA。QoS设置为1,那么broker至少会发一次消息给到clientA,给不到就重发。QoS设置为2,那么broker保证有且只有一次消息给到clientA。
但是有一个问题需要注意一下,对于同一个topic,如果client1以QoS 2等级发布,clientA以QoS 0等级订阅,那么clientA最多只会收到一条消息,即两者质量等级取最低。同样的,如果clinet1以QoS 0发布一条消息,clientA以QoS 2等级去订阅,clientA仍然最多只会收到一条消息,因为原则是两者QoS取最低。所以如果要想达到真正的QoS 2质量,就要保证publish和subscribe都设置为 QoS 2
3.抓包
下面结合抓包做一些直观的展示
192.168.2.195为客户端
211.159.189.50为服务端
3.1 connect 连接请求报文
从截图中可以看出,MQTT是基于TCP的,MQTT连接前先进行的是TCP的三次握手建立TCP连接
客户端通过发送MQTT连接控制报文向服务端发起连接请求,控制报文类型为0x10,可变长报头包含协议名、保活时间等信息,有效载荷包含客户端ID信息
3.2 CONNACK – 确认连接请求
服务端给客户端发送确认信息
3.3 订阅请求
3.4 订阅请求应答
3.5 发布消息
因为发布的消息服务质量等级为 QoS 0,所以无响应
现在我们将publish 的服务质量等级设为 QoS 1
再次抓包
发现每一个publish的消息服务端都会有一个回应
再将服务质量等级提升到 QoS 2
抓包
和上面分析的QoS 2一致,每次消息传递都需要四次交互,即(publish)Publish Message、(PUBREC)Publish Received、(PUBREL)Publish Release、(PUBCOMP)Publish Complete
3.6 还想看一下subscribe的QoS
我在客户端同时发布和订阅了相同的topic,服务质量等级都设为QoS 2,
截图中,方框框住的上面四条为客户端向服务端publish消息时产生的交互,满足上面的QoS 2等级的分析。下面四条为客户端订阅了topic而收到消息时的交互,同时也验证了客户端订阅就相当于服务端发布的猜想。
参考文章:https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels