基于标准MQTT 3.1版本
MQTT Qos服务质量等级降级的情况
QoS等于0的PUBLISH报文不能包含报文标识符 [MQTT-2.3.1-5]。
QoS 1的PUBLISH对应的是PUBACK,QoS 2的PUBLISH对应的是PUBCOMP,与SUBSCRIBE或UNSUBSCRIBE对应的分别是SUBACK或UNSUBACK [MQTT-2.3.1-3]。发送一个QoS 0的PUBLISH报文时, 相同的条件也适用于服务端 [MQTT-2.3.1-4]。
3.3.5 动作 Actions
客户端使用PUBLISH报文发送应用消息给服务端,目的是分发到其它订阅匹配的客户端。服务端使用PUBLISH报文发送应用消息给每一个订阅匹配的客户端。
客户端使用带通配符的主题过滤器请求订阅时,客户端的订阅可能会重复,因此发布的消息可能会匹配多个过滤器。对于这种情况,服务端必须将消息分发给所有订阅匹配的QoS等级最高的客户端 [MQTT-3.3.5-1]。服务端之后可以按照订阅的QoS等级,分发消息的副本给每一个匹配的订阅者。
收到一个PUBLISH报文时,接收者的动作取决于4.3节描述的QoS等级。
如果服务端实现不授权某个客户端发布PUBLISH报文,它没有办法通知那个客户端。它必须按照正常的QoS规则发送一个正面的确认,或者关闭网络连接[MQTT-3.3.5-2]。
MQTT消息订阅时 SUBSCRIBE - 订阅主题
3.8.3 有效载荷
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。
SUBSCRIBE报文有效载荷中的主题过滤器列表必须是1.5.3节定义的UTF-8字符串 [MQTT-3.8.3-1]。服务端应该支持包含通配符(4.7.1节定义的)的主题过滤器。如果服务端选择不支持包含通配符的主题过滤器,必须拒绝任何包含通配符过滤器的订阅请求 [MQTT-3.8.3-2]。
每一个过滤器后面跟着一个字节,这个字节被叫做服务质量要求(Requested QoS)。它给出了服务端向客户端发送应用消息所允许的最大QoS等级。SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器和QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的[MQTT-3.8.3-3]。有关错误处理的信息请查看4.8节。
请求的最大服务质量等级字段编码为一个字节,它后面跟着UTF-8编码的主题名,那些主题过滤器 / 和QoS等级组合是连续地打包。
服务端可以授予比订阅者要求的低一些的QoS等级。为响应订阅而发出的消息的有效载荷的QoS必须是原始发布消息的QoS和服务端授予的QoS两者中的最小值。如果原始消息的QoS是1而被授予的最大QoS是0,允许服务端重复发送一个消息的副本给订阅者 [MQTT-3.8.4-6]。
服务端发送给客户端的SUBACK报文对每一对主题过滤器 和QoS等级都必须包含一个返回码。这个返回码必须表示那个订阅被授予的最大QoS等级,或者表示这个订阅失败 [MQTT-3.8.4-5]。服务端可以授予比订阅者要求的低一些的QoS等级。
可以看出订阅者请求订阅的等级可能会低于自己所要求的。
我不知道这个规则的必要性,不过只说了“可以授予”,所以服务器也可以实现订阅者要求什么QOS就给什么QOS。我想大多数服务器也是这样干的,不会又去搞个降低等级功能,给业务增加复杂度。
订阅者和发布者的QOS等级是可以不一样的,发布者发布的QOS级别只有服务器肯定达到期望级别,而订阅者不一定按发布者的QOS级别收消息,因为订阅者也有一个自己的QOS等级,服务器转发消息给订阅者时按发布者和订阅者两者之间最小的QOS等级来发布。
所以发布者发布QOS2消息时,最终的订阅者可能根本收不到(如果订阅者QOS=0),而只有服务器收到且只收一次。
发布者发布QOS1消息时,最终的订阅者也有可能收不到(如果订阅者QOS=0),而服务器肯定收到且可能重复。
3.8.4 响应
服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应[MQTT-3.8.4-1]。SUBACK报文必须和等待确认的SUBSCRIBE报文有相同的报文标识符[MQTT-3.8.4-2]。
允许服务端在发送SUBACK报文之前就开始发送与订阅匹配的PUBLISH报文。如果服务端收到一个SUBSCRIBE报文,报文的主题过滤器与一个现存订阅的主题过滤器相同,那么必须使用新的订阅彻底替换现存的订阅。新订阅的主题过滤器和之前订阅的相同,但是它的最大QoS值可以不同。与这个主题过滤器匹配的任何现存的保留消息必须被重发,但是发布流程不能中断[MQTT-3.8.4-3]。
如果主题过滤器不同于任何现存订阅的过滤器,服务端会创建一个新的订阅并发送所有匹配的保留消息。
如果服务端收到包含多个主题过滤器的SUBSCRIBE报文,它必须如同收到了一系列的多个SUBSCRIBE报文一样处理那个,除了需要将它们的响应合并到一个单独的SUBACK报文发送[MQTT-3.8.4-4]。
服务端发送给客户端的SUBACK报文对每一对主题过滤器 和QoS等级都必须包含一个返回码。这个返回码必须表示那个订阅被授予的最大QoS等级,或者表示这个订阅失败[MQTT-3.8.4-5]。服务端可以授予比订阅者要求的低一些的QoS等级。为响应订阅而发出的消息的有效载荷的QoS必须是原始发布消息的QoS和服务端授予的QoS两者中的最小值。如果原始消息的QoS是1而被授予的最大QoS是0,允许服务端重复发送一个消息的副本给订阅者[MQTT-3.8.4-6]。
非规范示例
对某个特定的主题过滤器, 如果正在订阅的客户端被授予的最大QoS等级是1,那么匹配这个过滤器的QoS等级0的应用消息会按QoS等级0分发给这个客户端。这意味着客户端最多收到这个消息的一个副本。从另一方面说,发布给同一主题的QoS等级2
的消息会被服务端降级到QoS等级1再分发给客户端, 因此客户端可能会收到重复的消息副本。
如果正在订阅的客户端被授予的最大QoS等级是0, 那么原来按QoS等级2发布给客户端的应用消息在繁忙时可能会丢失,但是服务端不应该发送重复的消息副本。发布给同一主题的QoS等级1的消息在传输给客户端时可能会丢失或重复。
非规范评注
使用QoS等级2订阅一个主题过滤器等于是说: 我想要按照它们发布时的QoS等级接受匹配这个过滤器的消息。这意味着,确定消息分发时可能的最大QoS等级是发布者的责任,而订阅者可以要求服务端降低QoS到更适合它的等级。
发布publish和订阅subscribe都可以指定Qos等级。
publish消息时,指定的Qos是跟服务器有关系的,比如Qos 2是保证服务器只收到一次,而不是最终的订阅者。
订阅者在subscribe时虽然指定了Qos,但是收到的消息的Qos是服务器所支持的,不一定就是指定Qos等级的消息,而可能是降级了。
客户端订阅主题消息的Qos,是取决于原始发布消息的Qos和服务端授予的Qos两者中的最小值,且一定不会大于客户订阅的Qos。
比如sublish Qos 2,publish Qos 0,此时服务器转发的消息是Qos 0级别也就是subscribe可能收到一次消息也可能收不到。
再如sublish Qos 0,publish Qos 2,此时服务器转发的消息也是Qos 0级别,subscribe也是可能只收到一次消息或者收不到。
也就是服务器只会按publish和subcribe两者Qos等级最小的那个Qos规则来发送消息。
publish时指定的Qos是服务器肯定按此规则接收,但是最终订阅者不一定。
subscribe时指定的Qos表示订阅者可以接收的最高消息等级,也就是可能收到更低等级的消息。
总之一句话,Qos是会降级的,降级后的Qos也可能收到重复的主题消息。