我们研究了MQTT,以及其他各种消息传递协议,不久前.讨论中简要提到了MQTT具有服务质量(QoS)特性——其中之一是有争议的。
MQTT的可选QoS级别保证传递不超过一次(在这种情况下您可能会错过一条消息)、至少一次(在这种情况下您可能会得到一条重复的消息)或恰好一次。后者是争论的主题:各种讨论中的各种线索都在争论为什么它在实践中是不可能的。
这可以在多个层面上讨论。在协议本身中,这些QoS级别是通过确认序列实现的。
对于“至少一次”服务,发送方必须保留消息的副本,直到从接收方接收到PUBACK。在没有PUBACK的情况下,必须重新发送消息。重发有两种可能的原因:原始消息可能确实没有收到,或者PUBACK可能没有收到。在后一种情况下,收到了原件并将其发送出去进行处理,但发送者并不知道这一点。所以它发送了一个副本,但是接收者没有记录这是一个副本消息;对它来说,这只是一个新的信息。所以它会像处理第一个一样处理它。
图1。“至少一次”交付。注意,在第三个场景中,消息被发送两次以进行处理(虚线箭头)。
恰好一次稍微复杂一些。接收方在接收到消息后如何处理消息有几个选择,我不想在这方面迷失方向,因此我将抽象接收方行为(该图选择了一个选项)。如上所述,发送方发送消息并保留一份副本。当接收方收到消息时,它将PUBREC发送回发送方,但它暂时保留了接收记录。一旦发送方获得PUBREC,它就可以删除它的消息副本,但是它会暂时保留PUBREC的记录,并将PUBREL发送回接收方,让它知道它知道接收方获得了它的副本。一旦接收方得到PUBREL,它就会发送回一个PUBCOMP并丢弃它的消息记录;当发送方得到PUBCOMP时,它会丢弃它的PUBREC记录,交换终止。
图2,只交付一次。显示了一个消息处理选项。请注意,在任何情况下,消息都不会发送一次以上以供处理(虚线箭头)。
其基本思想是,发送方和接收方都保留状态记录,直到他们都确信消息已经发送。如果在此过程中,其中一个ack丢失,发送了重复的消息,接收方仍在跟踪该消息,因此可以忽略重复的消息。
从有限的消息传递角度来看,这一切都很好,但消息是有目的的;据推测,他们的意图是让信息产生某种结果。这些在讨论中被古怪地称为“副作用”,它们在计算机科学的意义上是“副作用”,但它们实际上可能是信息所期望的“主要效果”。
如果消息指示烤箱改变其温度,那么,从消息发送的角度来看,消息发送后温度改变的事实是一个副作用。但从系统的角度来看,这是主要的影响;这就是这条信息的目的。所以这里有一个责任的问题:如果消息没有到达,那么显然有一个消息传递的问题。但是如果信息到达了,但是在改变温度的过程中出了问题,谁来负责呢?
MQTT在接收端确认它收到了消息后就会立即处理这个问题。如果在接收系统中进一步发送消息的过程发生故障,或者其他一些系统元素发生故障,那么实际上,您仍然需要重新发送消息。但是,如果接收并处理了“降低10度”消息,但内部确认循环中的某些东西失败了,那么该消息可能会被处理两次,最终结果是温度降低20度。
最基本的问题是,在这个过程中有很多步骤都可能发生失败。即使在冗余或故障转移的情况下,在检测问题或执行切换时也通常会有延迟,并且在这些时间间隔中可能会出错。
这里的解决方案回到了“幂等性”:引用维基百科的话,这是“数学和计算机科学中某些运算的性质,可以应用多次而不改变初始应用之外的结果。”你必须在接收端做一些工作来实现这一点。
从本质上讲,接收方必须完全拥有国家所有权;它不能指望发送方共享状态的存储,以防发送方和接收方之间丢失状态更改。这是REST体系结构背后的原则,在HTTP协议的工作方式中可能很熟悉。
你也可以认为,与其发送“降低10度”的指令,消息应该说,“设置到这个新温度”(正好低10度)。但这将国家责任转移到了发送者身上。如果接收者的真实状态改变了,而发送者没有发现,那么状态实际上是未定义的。
让我们回到MQTT,问题是协议仅仅在只传递一次消息时就认为自己成功了。这是一种狭隘的观点。如果可靠性重要到需要使用该QoS级别,那么就不能依赖QoS,因为它只提供消息保证,而不是系统保证。
(至少,这是我对整个辩论的看法……)