3.8 SUBSCRIBE - Subscribe to topics
3.8.4 Response
When the Server receives a SUBSCRIBE Packet from a Client, the Server MUST respond with a SUBACK Packet [MQTT-3.8.4-1]. The SUBACK Packet MUST have the same Packet Identifier as the SUBSCRIBE Packet that it is acknowledging [MQTT-3.8.4-2].
The Server is permitted to start sending PUBLISH packets matching the Subscription before the Server sends the SUBACK Packet.
If a Server receives a SUBSCRIBE Packet containing a Topic Filter that is identical to an existing Subscription’s Topic Filter then it MUST completely replace that existing Subscription with a new Subscription. The Topic Filter in the new Subscription will be identical to that in the previous Subscription, although its maximum QoS value could be different. Any existing retained messages matching the Topic Filter MUST be re-sent, but the flow of publications MUST NOT be interrupted [MQTT-3.8.4-3].
Where the Topic Filter is not identical to any existing Subscription’s filter, a new Subscription is created and all matching retained messages are sent.
If a Server receives a SUBSCRIBE packet that contains multiple Topic Filters it MUST handle that packet as if it had received a sequence of multiple SUBSCRIBE packets, except that it combines their responses into a single SUBACK response [MQTT-3.8.4-4].
The SUBACK Packet sent by the Server to the Client MUST contain a return code for each Topic Filter/QoS pair. This return code MUST either show the maximum QoS that was granted for that Subscription or indicate that the subscription failed [MQTT-3.8.4-5]. The Server might grant a lower maximum QoS than the subscriber requested. The QoS of Payload Messages sent in response to a Subscription MUST be the minimum of the QoS of the originally published message and the maximum QoS granted by the Server. The server is permitted to send duplicate copies of a message to a subscriber in the case where the original message was published with QoS 1 and the maximum QoS granted was QoS 0 [MQTT-3.8.4-6].
Non normative examples If a subscribing Client has been granted maximum QoS 1 for a particular Topic Filter, then a QoS 0 Application Message matching the filter is delivered to the Client at QoS 0. This means that at most one copy of the message is received by the Client. On the other hand a QoS 2 Message published to the same topic is downgraded by the Server to QoS 1 for delivery to the Client, so that Client might receive duplicate copies of the Message.
If the subscribing Client has been granted maximum QoS 0, then an Application Message originally published as QoS 2 might get lost on the hop to the Client, but the Server should never send a duplicate of that Message. A QoS 1 Message published to the same topic might either get lost or duplicated on its transmission to that Client.
Non normative comment
Subscribing to a Topic Filter at QoS 2 is equivalent to saying “I would like to receive Messages matching this filter at the QoS with which they were published”. This means a publisher is responsible for determining the maximum QoS a Message can be delivered at, but a subscriber is able to require that the Server downgrades the QoS to one more suitable for its usage.
3.8 SUBSCRIBE - 订阅主题
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到更适合它的等级。
以下一段有点问题:
服务端可以授予比订阅者要求的低一些的QoS等级。为响应订阅而发出的消息的有效载荷的QoS**必须**是原始发布消息的QoS和服务端授予的QoS两者中的最小值。如果原始消息的QoS是1而被授予的最大QoS是0,允许服务端重复发送一个消息的副本给订阅者 [MQTT-3.8.4-6]。
前面说的没问题,订阅的人请求的等级,服务端不一定就给允其订阅的等级,而可能给予更低的等级,也就是说客户端订阅了QOS1,但是服务端返回成功订阅了QOS0.
但是下面两段话自相矛盾:
为响应订阅而发出的消息的有效载荷的QoS**必须**是原始发布消息的QoS和服务端授予的QoS两者中的最小值。
这个意思是publish者发布的是QOS1,订阅者是订阅的是QOS0
,那么就必须按QOS0发出。而如果订阅者订阅的是QOS1,发布者发布的是QOS1,也必须按QOS0发出。
那么这样就跟下面一句话有矛盾:
The server is permitted to send duplicate copies of a message to a subscriber in the case where the original message was published with QoS 1 and the maximum QoS granted was QoS 0
如果原始消息的QoS是1而被授予的最大QoS是0,允许服务端重复发送一个消息的副本给订阅者
QOS0是只发一次,是不可能重复的,那么怎么又说允许服务端重复发送一个消息的副本给订阅者呢?所以这句话是错误的!
其实这里是我原本的理解有问题,QOS0是只发一次的确是不可能重复的,但是发布者发布QOS1给服务器时有可能重复,然后如果服务器收到两个QOS1重复的消息那么也会两次按QOS0发给订阅者,所以文档说的这句话是正确的,shit!
特意查看了英文原版,发现中文版翻译的没有问题(事实上中文版翻译 的很好)。
而文档中紧跟着的非规范示例和非规范评注确实也是证实了是服务器按QOS等级最小规则的来转发消息。
经过测试,surgemq对于以上QOS的理解是有误的,如果订阅者订阅了QOS0,发布者发送的是QOS1或者QOS2,那么订阅者是收不到消息的,订阅者只能收到发布者发布的QOS0消息。
而测试mosquitto没有这个问题。
服务端发送给客户端的SUBACK报文对每一对主题过滤器 和QoS等级都必须包含一个返回码。这个返回码必须表示那个订阅被授予的最大QoS等级,或者表示这个订阅失败 [MQTT-3.8.4-5]。服务端可以授予比订阅者要求的低一些的QoS等级。
可以看出订阅者请求订阅的等级可能会低于自己所要求的。
我不知道这个规则的必要性,不过只说了“可以授予”,所以服务器也可以实现订阅者要求什么QOS就给什么QOS。我想大多数服务器也是这样干的,不会又去搞个降低等级功能,给业务增加复杂度。
订阅者和发布者的QOS等级是可以不一样的,发布者发布的QOS级别只有服务器肯定达到期望级别,而订阅者不一定按发布者的QOS级别收消息,因为订阅者也有一个自己的QOS等级,服务器转发消息给订阅者时按发布者和订阅者两者之间最小的QOS等级来发布。
所以发布者发布QOS2消息时,最终的订阅者可能根本收不到(如果订阅者QOS=0),而只有服务器收到且只收一次。
发布者发布QOS1消息时,最终的订阅者也有可能收不到(如果订阅者QOS=0),而服务器肯定收到且可能重复。