解密MQTT协议:从QOS到消息传递的全方位解析
解密MQTT协议:从QOS到消息传递的全方位解析
MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,广泛应用于物联网领域。其中,QoS(Quality of Service)机制是MQTT协议的重要组成部分,用于确保消息的可靠传输。本文将从QoS的定义开始,逐步深入解析QoS 0、QoS 1和QoS 2三种服务质量等级的原理和应用场景。
1、QoS介绍
1.1、QoS简介
使用MQTT协议的设备大部分都是运行在网络受限的环境下,而只依靠底层的TCP传输协议,并不能完全保证消息的可靠到达。MQTT提供了QoS机制,其核心是设计了多种消息交互机制来提供不同的服务质量,来满足用户在各种场景下对消息可靠性的要求。
MQTT定义了三个QoS等级,分别为:
- QoS 0,最多交付一次 -----> 可能丢失消息
- QoS 1,至少交付一次 -----> 可以保证收到消息,但消息可能重复
- QoS 2,只交付一次 -----> 可以保证消息既不丢失也不重复
QoS等级是由发布者在PUBLISH报文中指定的,大部分情况下Broker向订阅者转发消息时都会维持原始的QoS不变。不过也有一些例外的情况,根据订阅者的订阅要求,消息的QoS等级可能会在转发的时候发生降级。例如,订阅者在订阅时要求Broker可以向其转发的消息的最大QoS等级为QoS 1,那么后续所有QoS 2消息都会降级至QoS 1转发给此订阅者,而所有QoS 0和QoS 1消息则会保持原始的QoS等级转发。
1.2、QoS 0原理介绍
1.2.1、通讯原理说明
QoS 0是最低的QoS等级。QoS 0消息即发即弃,不需要等待确认,不需要存储和重传,因此对于接收方来说,永远都不需要担心收到重复的消息。
涉及到的相关报文:
1.2.2、Qos 0消息丢失原因
当我们使用QoS 0传递消息时,消息的可靠性完全依赖于底层的TCP协议。而TCP只能保证在连接稳定不关闭的情况下消息的可靠到达,一旦出现连接关闭、重置,仍有可能丢失当前处于网络链路或操作系统底层缓冲区中的消息。这也是QoS 0消息最主要的丢失场景。
1.3、Qos 1原理介绍
1.3.1、通讯原理说明
为了保证消息到达,QoS 1加入了应答与重传机制,发送方只有在收到接收方的PUBACK报文以后,才能认为消息投递成功,在此之前,发送方需要存储该PUBLISH报文以便下次重传。
QoS 1需要在PUBLISH报文中设置Packet ID,而作为响应的PUBACK报文,则会使用与PUBLISH报文相同的Packet ID,以便发送方收到后删除正确PUBLISH报文缓存。
涉及到的相关报文:
1.3.2、Qos 1消息会重复原因
对于发送方来说,没收到PUBACK报文分为以下两种情况:
- PUBLISH未到达接收方
- PUBLISH已经到达接收方,接收方的PUBACK报文还未到达发送方
在第一种情况下,发送方虽然重传了PUBLISH报文,但是对于接收方来说,实际上仍然仅收到了一次消息。
在第二种情况下,在发送方重传时,接收方已经收到过了这个PUBLISH报文,这就导致接收方将收到重复的消息。
重传PUBLISH报文的时候,PUBLISH中的DUP标志会被设置为1,用以表示这是一个重传的报文。
1.4、Qos 2原理介绍
1.4.1、通讯原理说明
QoS 2解决了QoS 0、1消息可能丢失或者重复的问题,但相应地,它也带来了最复杂的交互流程和最高的开销。每一次的QoS 2消息投递,都要求发送方与接收方进行至少两次请求/响应流程。
流程说明:
- 首先,发送方存储并发送QoS为2的PUBLISH报文以启动一次QoS 2消息的传输,然后等待接收方回复PUBREC报文。这一部分与QoS 1基本一致,只是响应报文从PUBACK变成了PUBREC。
- 当发送方收到PUBREC报文,即可确认对端已经收到了PUBLISH报文,发送方将不再需要重传这个报文,并且也不能再重传这个报文。所以此时发送方可以删除本地1存储的PUBLISH报文,然后发送一个PUBREL报文,通知对端自己准备将本次使用的Packet ID标记为可用了。与PUBLISH报文一样,我们需要确保PUBREL报文到达对端,所以也需要一个响应报文,并且这个PUBREL报文需要被存储下来以便后续重传。
- 当接收方收到PUBREL报文,也可以确认在这一次的传输流程中不会再有重传的PUBLISH报文到达,因此回复PUBCOMP报文表示自己也准备好将当前的PacketID用于新的消息了。
- 当发送方收到PUBCOMP报文,这一次的QoS 2消息传输就算正式完成了。在这之后,发送方可以再次使用当前的Packet ID发送新的消息,而接收方再次收到使用这个Packet ID的PUBLISH报文时,也会将它视为一个全新的消息。
涉及到的报文:
1.4.2、Qos 2消息不会重复原因
消息不丢失原因:与QoS 1相同
消息不会重复原因:
快速回顾一下QoS 1消息无法避免重复的原因:当我们使用QoS 1消息时,对接收方来说,回复完PUBACK这个响应报文以后Packet ID就重新可用了,也不管响应是否确实已经到达了发送方。所以就无法得知之后到达的,携带了相同Packet ID的PUBLISH报文,到底是发送方因为没有收到响应而重传的,还是发送方因为收到了响应所以重新使用了这个Packet ID发送了一个全新的消息。
所以,消息去重的关键就在于,通信双方如何正确地同步释放Packet ID,换句话说,不管发送方是重传消息还是发布新消息,一定是和对端达成共识了的。而QoS 2中增加的PUBREL流程,正是提供了帮助通信双方协商Packet ID何时可以重用的能力。
QoS 2规定,发送方只有在收到PUBREC报文之前可以重传PUBLISH报文。一旦收到PUBREC报文并发出PUBREL报文,发送方就进入了Packet ID释放流程,不可以再使用当前Packet ID重传PUBLISH报文。同时,在收到对端回复的PUBCOMP报文确认双方都完成Packet ID释放之前,也不可以使用当前Packet ID发送新的消息。
因此,对于接收方来说,能够以PUBREL报文为界限,凡是在PUBREL报文之前到达的PUBLISH报文,都必然是重复的消息;而凡是在PUBREL报文之后到达的PUBLISH报文,都必然是全新的消息。一旦有了这个前提,我们就能够在协议层面完成QoS 2消息的去重。