MQTT协议之消息流

1 MQTT介绍

MQTT(Message Queuing Telemetry Transport)是一个轻量级的消息传输协议,设计用于低带宽和不可靠的网络环境中。它在物联网(IoT)和移动应用领域广受欢迎,因其简单性和高效性在资源受限的设备上特别有用。以下是对MQTT的详细介绍:

1.1 MQTT的基本概念

  • 轻量级协议: MQTT是一个轻量级的发布/订阅(publish/subscribe)消息协议,适用于带宽有限、延迟高、不稳定的网络环境。

  • 客户端-服务器架构: MQTT协议基于客户端-服务器架构。客户端向服务器(称为MQTT代理或经纪人)发布消息,并订阅感兴趣的主题来接收消息。

1.2 核心特性

  • 发布/订阅模式: 在这种模式下,消息的发送者(发布者)和接收者(订阅者)不需要直接相连,它们通过主题进行通信。

  • 轻量级消息: MQTT设计了非常小的控制包,以减少网络流量和设备资源消耗。

  • 质量等级(Quality of Service, QoS): 提供三种消息交付质量等级,确保在不同的网络环境下满足不同的消息传输需求。

  • 持久会话: 支持持久会话,即使客户端断开连接,一旦重连,也能接收到未接收的消息。

1.3 协议操作

  • 连接(Connect): 客户端通过发送CONNECT消息到MQTT代理来建立连接。

  • 发布(Publish): 客户端可以发布消息到一个特定的主题。

  • 订阅(Subscribe): 客户端订阅一个或多个主题,以接收发布到这些主题的消息。

  • 断开(Disconnect): 客户端发送DISCONNECT消息断开与MQTT代理的连接。

1.4 应用场景

  • 物联网(IoT): 在IoT领域,MQTT用于连接各种资源受限的设备,如传感器、智能家居设备等。

  • 移动应用: 由于其低功耗特性,MQTT在移动应用中用于推送通知和即时消息。

  • 远程监控: 在远程监控系统中,MQTT用于传输实时监控数据。

  • 车联网: 在车联网应用中,MQTT用于车辆和基础设施之间的通信。

1.5 技术优势

  • 效率高: MQTT的设计最小化了网络带宽的需求和设备的电量消耗。

  • 易于实现: 协议简单,易于在各种设备上实现。

  • 可靠性: 支持不同等级的消息传递保证,从而提高通信的可靠性。

  • 安全性: 可以通过SSL/TLS实现数据的加密和安全通信。

1.6 发展和挑战

  • 标准化: MQTT已被OASIS标准化,为版本5.0和更高版本添加了更多的特性。

  • 安全性挑战: 随着在IoT中的广泛应用,确保数据安全和隐私成为一个重要的挑战。

  • 集成和互操作性: 与其他通信协议和平台的集成以及跨平台的互操作性是发展的关键。

MQTT以其轻量级、高效率和易于实现的特性,在物联网和移动应用领域得到了广泛应用。随着技术的发展,MQTT将继续发挥其在低带宽和不稳定网络环境下的通信优势,并在保证安全性和可靠性方面面临新的挑战和机遇。

2 MQTT消息流

2.1 网络故障

在任何网络环境下,都会出现一方连接失败,比如离开公司大门那一刻没有了WIFI信号。但持续连接的另一端-服务器可能不能立即知道对方已断开。类似网络异常情况,都有可能在消息发送的过程中出现,消息发送出去,就丢失了。

MQTT协议假定客户端和服务器端稳定情况一般,彼此之通信管道不可靠,一旦客户端网络断开,情况就会很严重,很难恢复原状。

但别忘记,很多客户端会有永久性存储设备支持,比如闪存ROM、存储卡等,在通信出现异常的情况下可以用于保存关键数据或状态信息等。

总之,异常网络情况很复杂,只能小心处理之。

2.2 消息重发策略

QoS > 0情况下,PUBLISH、PUBREL、SUBSCRIBE、UNSUBSCRIBE等类型消息在发送者发送完之后,需要等待一个响应消息,若在一个指定时间段内没有收到,发送者可能需要重试。重发的消息,要求DUP标记要设置为1.

等待响应的超时应该在消息成功发送之后开始算起,并且等待超时应该是可以配置选项,以便在下一次重试的时候,适当加大。比如第一次重试超时10秒,下一次可能为20秒,再一次重试可能为60秒呢。当然,还要有一个重试次数限制的。

还有一种情况,客户端重新连接,但未在可变头部中设置clean session标记,但双方(客户端和服务器端)都应该重试先前未发送的动态消息(in-flight messages)。客户端不被强制要求发送未被确认的消息,但服务器端就得需要重发那些未被去确认的消息。

QoS level决定的消息流

QoS level为Quality of Service level的缩写,翻译成中文,服务质量等级。

MQTT 3.1协议在"4.1 Quality of Service levels and flows"章节中,仅仅讨论了客户端到服务器的发布流程,不太完整。因为决定消息到达率,能够提升发送质量的,应该是服务器发布PUBLISH消息到订阅者这一消息流方向。

QoS level 0

至多发送一次,发送即丢弃。没有确认消息,也不知道对方是否收到。

ClientMessage and directionServer
QoS = 0PUBLISH
---------->
Action: Publish message to subscribers then Forget
Reception: <=1

针对的消息不重要,丢失也无所谓。

网络层面,传输压力小。

QoS level 1

所有QoS level 1都要在可变头部中附加一个16位的消息ID。

SUBSCRIBE和UNSUBSCRIBE消息使用QoS level 1。

针对消息的发布,Qos level 1,意味着消息至少被传输一次。

发送者若在一段时间内接收不到PUBACK消息,发送者需要打开DUB标记为1,然后重新发送PUBLISH消息。因此会导致接收方可能会收到两次PUBLISH消息。针对客户端发布消息到服务器的消息流:

ClientMessage and directionServer
QoS = 1
DUP = 0
Message ID = x

Action: Store message

PUBLISH
---------->
Actions:
  • Store message

  • Publish message to subscribers
  • Delete message

Reception: >=1

Action: Discard messagePUBACK
<----------

Message ID = x

针对服务器发布到订阅者的消息流:

ServerMessage and directionSubscriber
QoS = 1
DUP = 0
Message ID = x
PUBLISH
---------->
Actions:
  • Store message

  • Make message available                       

Reception: >=1

PUBACK
<----------

Message ID = x

发布者(客户端/服务器)若因种种异常接收不到PUBACK消息,会再次重新发送PUBLISH消息,同时设置DUP标记为1。接收者以服务器为例,这可能会导致服务器收到重复消息,按照流程,broker(服务器)发布消息到订阅者(会导致订阅者接收到重复消息),然后发送一条PUBACK确认消息到发布者。

在业务层面,或许可以弥补MQTT协议的不足之处:重试的消息ID一定要一致接收方一定判断当前接收的消息ID是否已经接受过

但一样不能够完全确保,消息一定到达了。

QoS level 2

仅仅在PUBLISH类型消息中出现,要求在可变头部中要附加消息ID。

级别高,通信压力稍大些,但确保了仅仅传输接收一次。

先看协议中流程图,Client -> Server方向,会有一个总体印象:

ClientMessage and directionServer
QoS = 2
DUP = 0
Message ID = x

Action: Store message

PUBLISH
---------->
Action(a) Store message

or

Actions(b):
  • Store message ID
  • Publish message to subscribers
PUBREC
<----------
Message ID = x
Message ID = xPUBREL
---------->
Actions(a):
  • Publish message to subscribers
  • Delete message

or

Action(b): Delete message ID
Action: Discard messagePUBCOMP
<----------
Message ID = x

Server -> Subscriber

ServerMessage and directionSubscriber
QoS = 2
DUP = 0
Message ID = x
PUBLISH
---------->
Action: Store message
PUBREC
<----------
Message ID = x
Message ID = xPUBREL
---------->
Actions:
  • Make message available                       
PUBCOMP
<----------
Message ID = x

Server端采取的方案a和b,都包含了何时消息有效,何时处理消息。两个方案二选一,Server端自己决定。但无论死采取哪一种方式,都是在QoS level 2协议范畴下,不受影响。若一方没有接收到对应的确认消息,会从最近一次需要确认的消息重试,以便整个(QoS level 2)流程打通。

2.3 消息顺序

消息顺序会受许多因素的影响,但对于服务器程序,必须保证消息传递流程的每个阶段要和开始的顺序一致。例如,在QoS level 2定义的消息流中,PUBREL流必须和PUBLISH流具有相同的顺序发送:

ClientMessage and directionServer
PUBLISH 1
---------->
PUBLISH 2
---------->
PUBLISH 3
---------->
PUBREC 1
<----------
PUBREC 2
<----------
PUBREL 1
---------->
PUBREC 3
<----------
PUBREL 2
---------->
PUBCOMP 1
<----------
PUBREL 3
---------->
PUBCOMP 2
<----------
PUBCOMP 3
<----------

流动消息(in-flight messages)数量允许有一个可保证的效果:

  • 在流动消息(in-flight)窗口1中,每个传递流在下一个流开始之前完成。这保证消息以提交的顺序传递
  • 在流动消息(in-flight)大于1的窗口,只能在QoS level内被保证消息的顺序

2.4 消息的持久化

在MQTT协议中,PUBLISH消息固定头部RETAIN标记,只有为1才要求服务器需要持久保存此消息,除非新的PUBLISH覆盖。

对于持久的、最新一条PUBLISH消息,服务器不但要发送给当前的订阅者,并且新的订阅者(new subscriber,同样需要订阅了此消息对应的Topic name)会马上得到推送。

Tip:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送,不是所有。

2.5 消息流的编码/解码

MQTT协议中,由目前定义的14种类型消息在客户端和服务器端之间数据进行交互。若以JAVA语言构建MQTT服务器,可选择Netty作为基础。

在Netty中,数据的进入和流出,代表了一次完整的交互。无论是要进入的还是要流出的数据(单独以服务器为例),都可看做字节流。若把每种类型消息抽象为一个具体对象,那么处理起来就不难了。

客户端->服务器,进入的字节流,逐个字节/单位读取,可还原成一个具体的消息对象(解码的过程)。

要发送到客户端的消息对象,转换(编码)成字节流,然后由TCP通道流转到接收者。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智慧医疗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值