MQTT 面试必问! QoS 服务质量

3,507 阅读6分钟

MQTT Quality Of Service 服务质量

什么是服务质量 QoS ?

QoS 是在消息发送方和消息接收方定义的一种协议,其用于保证特定消息的交付级别。

在 MQTT 中有三种 QoS 级别:

  • At most once (0) 最多一次
  • At least once (1) 至少一次
  • Exactly once (2) 恰好一次

那么完成这三种级别的消息交付,其付出的代价也是不一样的,我们在选择过程中需要根据实际的场景来进行考量。

在 MQTT 中聊 QoS 时消息流向需要从两个方向考虑:

  1. 消息发布客户端 -> 代理服务器
  2. 代理服务器 -> 消息订阅客户端

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-az4ZAWH6-1629294049492)(/Users/admin/Library/Application Support/typora-user-images/image-20210816172102085.png)]

为何如此?

客户端在发布消息到代理 borker 的时候定义了发布消息的 QoS 级别。而代理在将该消息传递给订阅客户端时使用的是订阅客户端在订阅主题时定义的 QoS ,如果订阅客户端的 QoS 比 发布客户端的 QoS 小则该消息会以较低的 QoS 级别进行消息的传递。

强调一下 QoS

QoS 是 MQTT 协议中一个极为关键的特性,QoS 使得服务端能够选择与其网络服务质量相匹配的服务级别。MQTT 将保障消息的可靠传输和重新发送(即便底层传输是不可靠的),QoS 使得在不可靠的网络中通信更加容易。

QoS 0 at most once 至多一次

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G6iIr3PI-1629294049496)(/Users/admin/Library/Application Support/typora-user-images/image-20210816175541055.png)]

最低的 QoS 级别为 0,有时也被叫做 “即发即忘”,消息不会被接受方确认收到,消息发送方也不会进行存储和重新传输。其提供与底层 TCP 协议相同的保证

QoS 1 at least once 至多一次

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OPxc6vwe-1629294049500)(/Users/admin/Library/Application Support/typora-user-images/image-20210816180950140.png)]

QoS 1 的级别会保证消息至少有一次可以传递给接受者,发送方存储消息直到他接受到接收方发送的 PUBACK (发布确认)包,消息可能被传递一次或者多次。

发送方会使用每个数据包中的 packetId 与对应的 PUBACK 数据包进行匹配,如果发送方在一个合理的时间段内没有收到 PUBACK 包则进行重新发送。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O88a8OCq-1629294049507)(/Users/admin/Library/Application Support/typora-user-images/image-20210817161257722.png)]

当接收方在收到 QoS 为 1 的消息时可以立即处理它,如 MQTT 代理 broker 在收到消息的时候可以立即进行处理,将消息发送给订阅者,然后发送 PUBACK 包进行确认。

重新发送 如果发送方进行了重新发送,它会设置 DUP 标志为 true。在 QoS 1 中 无论 DUP 如何消息的接收者都会发送 PUBACK 包 。

QoS 2 exactly once 恰好一次

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bn7P5PS1-1629294049512)(/Users/admin/Library/Application Support/typora-user-images/image-20210817000143328.png)]

QoS 2 是 MQTT 中级别最高的,这个级别保证消息送达且仅送达一次,QoS 2 是基于接发双方 4 部分握手来实现的,同时也就意味着这个级别,最安全也最低效。

消息发送方和消息接收方使用原始的 PUBLSH 包的 packetId 来协调消息的传递。

消息接收方 收到 消息发送方 的 PUBLSH QoS 2 的数据包时,它可以相应的处理发布消息并 发送 PUBREC (发布收到) 包 来响应 消息发送方 以确认 PUBLSH 包收到,如果发送方没有收到接收方发来的 PUBREC 包,它会重新发送带有 DUP 标识的 PUBLSH 包直到收到确认为止。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uh9Zg8ZZ-1629294049522)(/Users/admin/Library/Application Support/typora-user-images/image-20210817161159834.png)]

发送方接收 到 接收方PUBREC 响应 的时候,就可以丢弃原始的 PUBLSH 数据包 并以 PUBREL (发布释放)数据包 进行响应

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4QQdoPOE-1629294049523)(/Users/admin/Library/Application Support/typora-user-images/image-20210817161339145.png)]

接收方在收到 PUBREL 数据包后就可以丢弃所有相关的存储状态并发送 PUBCOMP (发布完成)数据包给发送方,发送方收到 PUBCOMP 即可丢弃全部相关存储状态。

接收方在未收到 PUBREL 并返回 PUBCOMP 之前需要存储原始的 PUBLSH 的数据包标识符的引用,即 packetId,此用于防止再次处理数据。发送方在收到 PUBCOMP 后该 packetId 方可重复使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CS78jdmQ-1629294049525)(/Users/admin/Library/Application Support/typora-user-images/image-20210817161421110.png)]

当 QoS 2 流传输完成时,收发双方都确认消息已正确交付。

在此过程中如果消息包丢失,发送方有责任在合适的时间内进行重传。同样不论收发双方是 客户端 还是 broker 代理,它们都有责任响应收到的数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SIA3P6pF-1629294049526)(/Users/admin/Library/Application Support/typora-user-images/image-20210817182256469.png)]

那么 QoS 2 对比 QoS 1 来说记录了 packetId 来进行去重,这个时候就需要进行相关的资源释放所以增加了 PUBREL 和 PUBCOMP 对上两步进行确认收到以便进行资源的释放,其在协议层面进行了消息的去重,QoS 1 的级别则需要在 Broker 或者说 Server 方对消息包进行去重

总的来说,QoS 2 在协议层面实现了消息的去重,对于低内存的客户端物理设备是更加合适的。如果我们拥有足够的内存那么使用 QoS 1 在应用层的逻辑进行去重无疑是更好的选择。

必须要知道的事

QoS 的某些部分是容易忽略的,但这些点至关重要,你需要牢记。

QoS 降级

如上文提到的,我们在考虑消息传递的时候需要考虑两个方面:

客户端发布者 -> 服务端代理

服务端代理 -> 客户端订阅者

这里消息将以最低的 QoS 进行传递。

这意味着,当你想使用高级别的 QoS 时,需要确保发布订阅双方的 QoS 是一致的,或者将其逻辑在应用层进行处理

单个客户端的数据包标识符 packetId 是唯一的

在 QoS 1 和 QoS 2 级别下 每个客户端在与特定的代理进行交互的时候其 packetId 是唯一的,但在全部的客户端中不唯一。一旦流完成,该 packetId 将可以重用,这种重用机制是 packetId 不需要超过 65535 的原因,同理在为完成交互的情况下,发送超过这个数量的消息是不现实的。

最佳实践

什么时候使用 QoS 0 ?

你的应用在大部分情况下建立了稳定持久的连接。

你可以容忍消息有部分的丢失。

你不需要消息队列。只有在 QoS 1 和 2 时消息才会排队。

什么时候使用 QoS 1 ?

你需要确保每条消息,并且你的服务可以处理重复数据。QoS 1 是最常用的级别,它保证数据到达,但这会产生重复项,所以你的项目必须可以容忍重复或者可以妥善处理它。

你无法承受 QoS 2 的开销,QoS 1 传递消息的速度要比 QoS 2 快很多。

什么时候使用 QoS 2 ?

消息只传达一次对你的项目至关重要,你的项目无法自己妥善处理重复。

可以容忍 QoS 2 更长时间的交互。

可以关注 dying 搁浅 公众号,可以收到推送,如有技术问题和疑问也可以直接加我的微信 dyinggq 我们一起探讨。希望本文对你有所帮助。