MQTT理解

作者: 老鼠AI大米_Java全栈 | 来源:发表于2020-07-02 15:29 被阅读0次

    随着移动互联网普及,物联网(Internet of Things,IoT)也越来越流行。虽然HTTP是网页标准,不过机器之间(Machine-to-Machine,M2M)的大规模沟通需要不同的模式:之前的请求/回答(Request/Response)模式不再合适,取而代之的是发布/订阅(Publish/Subscribe)模式。这就是轻量级、可扩展的MQTT(Message Queuing Telemetry Transport)可以施展拳脚的舞台。

    MQTT简介

    MQTT是基于二进制消息的发布/订阅编程模式的消息协议,最早由IBM提出的,如今已经成为OASIS规范。由于规范很简单,非常适合需要低功耗和网络带宽有限的IoT场景,比如:

    • 遥感数据
    • 汽车
    • 智能家居
    • 智慧城市
    • 医疗医护
      由于物联网的环境是非常特别的,所以MQTT遵循以下设计原则:
    1. 精简,不添加可有可无的功能。
    2. 发布/订阅(Pub/Sub)模式,方便消息在传感器之间传递。
    3. 允许用户动态创建主题,零运维成本。
    4. 把传输量降到最低以提高传输效率。
    5. 把低带宽、高延迟、不稳定的网络等因素考虑在内。
    6. 支持连续的会话控制。
    7. 理解客户端计算能力可能很低。
    8. 提供服务质量管理。
    9. 假设数据不可知,不强求传输数据的类型与格式,保持灵活性。

    运用MQTT协议,设备可以很方便地连接到物联网云服务,管理设备并处理数据,最后应用到各种业务场景,如下图所示:


    0.png

    发布/订阅模式

    与请求/回答这种同步模式不同,发布/定义模式解耦了发布消息的客户(发布者)与订阅消息的客户(订阅者)之间的关系,这意味着发布者和订阅者之间并不需要直接建立联系。打个比方,你打电话给朋友,一直要等到朋友接电话了才能够开始交流,是一个典型的同步请求/回答的场景;而给一个好友邮件列表发电子邮件就不一样,你发好电子邮件该干嘛干嘛,好友们到有空了去查看邮件就是了,是一个典型的异步发布/订阅的场景。

    熟悉编程的同学一定非常熟悉这种设计模式了,因为它带来了这些好处:

    • 发布者与订阅者不比了解彼此,只要认识同一个消息代理即可。
    • 发布者和订阅者不需要交互,发布者无需等待订阅者确认而导致锁定。
    • 发布者和订阅者不需要同时在线,可以自由选择时间来消费消息。

    主题

    MQTT是通过主题对消息进行分类的,本质上就是一个UTF-8的字符串,不过可以通过反斜杠表示多个层级关系。主题并不需要创建,直接使用就是了。

    主题还可以通过通配符进行过滤。其中,+可以过滤一个层级,而*只能出现在主题最后表示过滤任意级别的层级。举个例子:

    • building-b/floor-5:代表B楼5层的设备。
    • +/floor-5:代表任何一个楼的5层的设备。
    • building-b/*:代表B楼所有的设备。
      注意,MQTT允许使用通配符订阅主题,但是并不允许使用通配符广播。

    消息类型

    MQTT拥有14种不同的消息类型:

    1. CONNECT:客户端连接到MQTT代理
    2. CONNACK:连接确认
    3. PUBLISH:新发布消息
    4. PUBACK:新发布消息确认,是QoS 1给PUBLISH消息的回复
    5. PUBREC:QoS 2消息流的第一部分,表示消息发布已记录
    6. PUBREL:QoS 2消息流的第二部分,表示消息发布已释放
    7. PUBCOMP:QoS 2消息流的第三部分,表示消息发布完成
    8. SUBSCRIBE:客户端订阅某个主题
    9. SUBACK:对于SUBSCRIBE消息的确认
    10. UNSUBSCRIBE:客户端终止订阅的消息
    11. UNSUBACK:对于UNSUBSCRIBE消息的确认
    12. PINGREQ:心跳
    13. PINGRESP:确认心跳
    14. DISCONNECT:客户端终止连接前优雅地通知MQTT代理

    服务质量QoS等级

    MQTT设计了一套保证消息稳定传输的机制,包括消息应答、存储和重传。在这套机制下,提供了三种不同层次QoS(Quality of Service):

    • QoS0,At most once,至多一次;
    • QoS1,At least once,至少一次;
    • QoS2,Exactly once,确保只有一次。

    QoS 是消息的发送方(Sender)和接受方(Receiver)之间达成的一个协议:

    • QoS0 代表,Sender 发送的一条消息,Receiver 最多能收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,也就算了;
    • QoS1 代表,Sender 发送的一条消息,Receiver 至少能收到一次,也就是说 Sender 向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,但是因为重传的原因,Receiver 有可能会收到重复的消息;
    • QoS2 代表,Sender 发送的一条消息,Receiver 确保能收到而且只收到一次,也就是说 Sender 尽力向 Receiver 发送消息,如果发送失败,会继续重试,直到 Receiver 收到消息为止,同时保证 Receiver 不会因为消息重传而收到重复的消息。

    注意:
    QoS是Sender和Receiver之间的协议,而不是Publisher和Subscriber之间的协议。换句话说,Publisher发布了一条QoS1的消息,只能保证Broker能至少收到一次这个消息;而对于Subscriber能否至少收到一次这个消息,还要取决于Subscriber在Subscibe的时候和Broker协商的QoS等级。

    QoS0

    QoS0等级下,Sender和Receiver之间一次消息的传递流程如下:


    1.png

    Sender向Receiver发送一个包含消息数据的PUBLISH包,然后不管结果如何,丢掉已发送的PUBLISH包,一条消息的发送完成。

    QoS1

    QoS1要保证消息至少到达一次,所以有一个应答的机制。Sender和Receiver的一次消息的传递流程如下:


    2.png
    1. Sender向Receiver发送一个带有数据的PUBLISH包,并在本地保存这个PUBLISH包;
    2. Receiver收到PUBLISH包以后,向Sender发送一个PUBACK数据包,PUBACK数据包没有消息体(Payload),在可变头中有一个包标识(Packet Identifier),和它收到的PUBLISH包中的Packet Identifier一致。
    3. Sender收到PUBACK之后,根据PUBACK包中的Packet Identifier找到本地保存的PUBLISH包,然后丢弃掉,一次消息的发送完成。

    但是消息传递流程中可能会出现问题:

    1. 如果Sender在一段时间内没有收到PUBLISH包对应的PUBACK,它将该PUBLISH包的DUP标识设为1(代表是重新发送的PUBLISH包),然后重新发送该PUBLISH包。
    2. Receiver可能会重复收到消息,需自行去重。

    QoS2

    相比QoS0和QoS1,QoS2不仅要确保Receiver能收到Sender发送的消息,还需要确保消息不重复。它的重传和应答机制就要复杂一些,同时开销也是最大的。QoS2下,一次消息的传递流程如下所示:


    3.png
    1. Sender发送QoS为2的PUBLISH数据包,数据包 Packet Identifier 为 P,并在本地保存该PUBLISH包;
    2. Receiver收到PUBLISH数据包后,在本地保存PUBLISH包的Packet Identifier P,并回复Sender一个PUBREC数据包,PUBREC数据包可变头中的Packet Identifier为P,没有消息体(Payload);
    3. 当Sender收到PUBREC,它就可以安全的丢弃掉初始Packet Identifier为P的PUBLISH数据包。同时保存该PUBREC数据包,并回复Receiver一个PUBREL数据包,PUBREL数据包可变头中的Packet Identifier为P,没有消息体;
    4. 当Receiver收到PUBREL数据包,它可以丢掉保存的PUBLISH包的Packet Identifier P,并回复Sender一个可变头中 Packet Identifier 为 P,没有消息体(Payload)的PUBCOMP数据包;
    5. 当Sender收到PUBCOMP包,那么认为传输已完成,则丢掉对应的PUBREC数据包;

    上面是一次完整无误的传输过程,然而传输过程中可能会出现以下情况:

    • 情况1:Sender发送PUBLISH数据包给Receiver的时候,发送失败;
    • 情况2:Sender已经成功发送PUBLISH数据包给Receiver了,但是Receiver发送PUBREC数据包失败;
    • 情况3:Sender已经成功收到了PUBREC数据包,但是PUBREL数据包发送失败;
    • 情况4:Receiver已经收到了PUBREL数据包,但是发送PUBCOMP数据包时发送失败

    针对上述的问题,较为详细的处理方法如下:

    • 不管是情况1还是情况2,因为Sender在一定时间内没有收到PUBREC,那么它会把PUBLISH包的DUP标识设为1,重新发送该PUBLISH数据包;
    • 不管是情况3还是情况4,因为Sender在一定时间内没有收到PUBCOMP包,那么它会重新发送PUBREL数据包;
    • 针对情况2,Receiver可能会收到多个重复的PUBLISH包,更加完善的处理如下:
      Receiver在收到PUBLISH数据包之后,马上回复一个PUBREC数据包。并会在本地保存PUBLISH包的Packet Identifier P,不管之后因为重传多少次这个Packet Identifier 为P的数据包,Receiver都认为是重复的,丢弃。同时Receiver接收到QoS为2的PUBLISH数据包后,并不马上投递给上层,而是在本地做持久化,将消息保存起来(这里需要是持久化而不是保存在内存)。
    • 针对情况4,更加完善的处理如下:
      Receiver收到PUBREL数据包后,正式将消息递交给上层应用层,投递之后销毁Packet Identifier P,并发送PUBCOMP数据包,销毁之前的持久化消息。之后不管接收到多少个PUBREL数据包,因为没有Packet Identifier P,直接回复PUBCOMP数据包即可。

    相关文章

      网友评论

        本文标题:MQTT理解

        本文链接:https://www.haomeiwen.com/subject/olulqktx.html