MQTT

发布时间 2023-12-29 17:17:55作者: 霍北北

MQTT

-

1. MQTT协议的定义

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IBM在1999年发布。
定义来源:菜鸟教程

通过定义,我们其实可以问出这样几个问题:什么是发布/订阅模式?MQTT协议的“轻量级”是如何体现的?如何构建在TCP/IP协议之上?解决了这三个问题,我们对于MQTT协议也就形成了一个基础的认知。

2. 发布/订阅模式

2.1 发布/订阅模式的运行流程

发布/订阅模式(Publish Subscribe Pattern)并非是MQTT协议所独有的一种概念,它本身是常见设计模式的一种。

MQTT发布/订阅模式的参与者主要有三个:发布者(Publisher),订阅者(Subscriber)以及代理(borker)。在消息传输过程中,发布者发送消息给到代理,代理接收到消息之后,再将消息转发给订阅者。消息传输的过程中,还有一个问题:代理如何判断这条消息的订阅者有哪些?MQTT中解决这个问题的方法是,引入Topic机制。发布者发送的每一条消息都包含一个topic信息,订阅者指的是实现订阅该topic的所有客户端。

添加一些实际的细节,我们再总结一下MQTT的流程:

  1. 发布者连接到代理
  2. 订阅者连接到代理
  3. 订阅者订阅topic
  4. 发布者向代理发送带有topic的消息
  5. 代理将收到的消息转发给订阅该topic的订阅者


图片来源:MQTT官网

其实我们想要理解发布/订阅模式,公众号是一个很好的例子。我们订阅一个公众号,并且收到该公众号推文的过程,跟我们所提到的模式是基本一致的,只是在topic机制上稍有区别:公众号作为一个发布者,只能发布一种topic的消息(公众号本身的名称就是它的topic),但是在MQTT中,发布者可以发布多种topic的消息。

还有一件事,从某种层面上来说,我们上文所提到的发布者和订阅者同属于客户端。其实在不同的通信过程中,发布者和订阅者的身份并不是固定不变的。比如在一个新的通信过程中,当前的订阅者发送新主题的消息,被当前的发布者所订阅,这时,这两个客户端的身份就颠倒过来了。很多物联网项目都包括双向通信,在此过程中,客户端其实都是兼有发布者、订阅者两重身份的,在不同的通信过程中所担任的角色不同。

思考一下我们正在开发的项目:设备通过MQTT上传温湿度信息,小程序获取到这部分信息并且展示给用户。在这个流程中,发布者是谁?订阅者是谁?

2.2. 发布/订阅模式的解耦特性

来源:阿里云开发者社区

发布/订阅模式最重要的是实现了发布者与订阅者之间的分离。这种解耦具有几个方面:

  • 空间解耦:发布者和订阅者不需要彼此了解(例如,无需交换IP地址和端口)。
  • 时间解耦:发布者和订阅者不需要同时运行。
  • 同步解耦:发布或接收期间,两个组件的操作都不需要中断。

空间解耦指的是发布者和订阅者并不需要直接与对方进行通信,不需要获取对方的信息,只需要知道代理的相关信息(主机名/IP地址和端口)即可。

时间解耦指的是发布者和订阅者不需要同时在线,可以异步工作,即代理可以为不在线的订阅者缓存发布者的消息。当然,大多数的MQTT用例都是在实时传递消息,但是代理在满足一定前提的情况下,可以为不在线的客户端存储消息。

同步解耦指的是MQTT大多数客户端(客户端包括发布者和订阅者)库都是异步工作的,在等待消息或发布消息的时不会阻塞其他任务。

3. MQTT协议的数据包格式

为什么我们说MQTT是一个“轻量级”的通讯协议呢?
其实就是因为它的协议数据包精简,通讯所需要发送的信息少。

MQTT协议数据包由三部分组成:

名称 英文名称 报文包含
固定报头 fixed header 所有控制报文都包含
可变报头 variable header 部分控制报文包含
消息体(有效载荷) payload 部分控制报文包含

3.1. 固定报头

来源:CSDN博客
更加详细的数据包格式请参考:
MQTT协议中文版 /
MQTT官网文档列表

固定报头是MQTT协议开头,2个字节,分为三个部分:标志位、报文类型、剩余长度,下图为固定报头结构示意图:

3.1.1. 标志位

数据包第1个字节的低4位表示标志位。

3.1.2. 报文类型

数据包第1个字节的高4位表示报文类型,报文类型一共分为16种,下图为具体的报文类型:

3.1.3. 剩余长度

第2个字节是剩余长度,其表示的就是在剩余长度之后的数据长度(还有多少个字节)。

剩余长度的字节数是不固定的,至少1个字节,最多4个字节,所以固定报头中包含的一个字节就是剩余长度的第一个字节。

因为表示剩余长度的字节数是一个动态值,所以剩余长度的每一个字节的最高位是一个标志位,用来表示下一个字节是否也属于剩余长度。
如果该字节的最高位是1,则表示下一个字节也用于表示剩余长度;如果该字节的最高位是0,则表示下一个字节不用于表示剩余长度。

多字节的剩余长度数值计算:

假设有3个表示剩余长度的字节,
分别是10010001、11001001、01001001

  1. 先去掉标志位,分别是0010001、10010001、1001001
  2. 按照后面的字节在高位的规则,合并成一个数据:1001001\10010001\0010001
  3. 二进制转十进制,得到剩余长度为1205393

3.2. 可变报头

可变报头的数据依据固定报头中的报文类型而不同,一般是包含和报文类型相关的数据;例如客户端连接服务器报文,包含了协议名,协议级别(用来表示mqtt的版本信息)、连接标志、保持连接。

3.3. 有效载荷

有效载荷内容也是根据报文类型的不同而不同,在上述可变报头的2张示意图中也能看出,连接服务器报文的有效载荷则包含了:客户端标识符、遗属主题、遗属消息、用户、密码,而发布消息报文,则仅仅包含了应用消息;不同的有效载荷解析方式有所不同,具体要看每一条报文的规定。

4. TCP/IP协议

首先,我们要清楚一点:TCP/IP不是一个协议,而是一个协议族的统称。

4.1. 网络协议分层


图片来源:CSDN TCP/IP协议

OSI参考模型是当时提出的一个理论上的模型,在现实中并没有真正应用。我们当前的网络连接大多数都是基于TCP/IP协议实现的,有些资料会将TCP/IP模型认为是5层,最底层还有一个物理层。

我们所学习到的MQTT协议就是处于这个TCP/IP协议的应用层部分。

5. 服务质量

来源:EMQ文档

服务质量,QoS(Quality of Service levels)是MQTT的一个重要特性。

当我们使用 TCP/IP 时,连接已经在一定程度上受到保护。但是在无线网络中,中断和干扰很频繁,MQTT 在这里帮助避免信息丢失及其服务质量水平。这些级别在发布时使用。

服务质量 解释 理解
QoS 0 至多一次 完全依赖底层TCP/IP,存在丢失消息的可能性,不可靠的信息传输
QoS 1 至少一次 加入了应答与重传机制,一定可以收到消息,但是可能重复
QoS 2 仅一次 QoS 2保证消息不会重复的机制与QoS 1是一致的,新添加了可以避免可以保证消息不会重复的机制,既不丢失也不重复

6. 其他

来源:EMQ遗嘱消息文档 / 稀土掘金关于MQTT基础入门

MQTT协议还有一些需要我们提到的的点:

  1. 消息的过滤类型:除了前文提到的可以通过topic进行过滤,还可以通过消息内容、消息类型进行过滤。
  2. 遗嘱消息机制:所谓的遗嘱消息就是在客户端正常连接时向代理指定的消息,当客户端连接异常断开的情况下,代理将遗嘱消息发送给订阅者。