MQTT介绍
MQTT(MQ Telemetry Transport)协议,是 IBM 公司在 1999 年开发的轻量级网络协议,采用的是发布 - 订阅通信模式,它有三个主要特点:
- 采用二进制的消息内容编码格式,所以二进制数据、JSON 和图片等负载内容都可以方便传输。
- 协议头很紧凑,协议交互也简单,保证了网络传输流量很小。
- 支持 3 种 QoS(Quality of Service,服务质量)级别,便于应用根据不同的场景需求灵活选择。
MQTT 协议非常适合计算能力有限、网络带宽低、信号不稳定的远程设备,所以它成为了物联网系统事实上的网络协议标准。
MQTT 自身的“基因”很强大
MQTT 协议作为物联网设备的“第一语言”,不仅是因为 MQTT 的生态完善,MQTT 协议本身的优秀设计也是重要的因素。
契合物联网大部分应用场景的发布 - 订阅模式。
采用了发布 - 订阅模式,MQTT 协议具有很多优点,比如能让一个传感器数据触发一系列动作;网络不稳定造成的临时离线不会影响工作;方便根据需求动态调整系统规模等。这使得它能满足绝大部分物联网场景的需求。
能够满足物联网中资源受限设备需要的轻量级特性。
MQTT 是一个轻量级的网络协议,这一点也是它在物联网系统中流行的重要原因。毕竟物联网中大量的都是计算资源有限、网络带宽低的设备。这种“轻量级”体现在两个方面。
一方面,MQTT 消息采用二进制的编码格式,而不是 HTTP 协议那样的文本的表述方式。在 HTTP 协议传输的这段文本中,每个字符都要占用 1 个字节。而如果使用 MQTT 协议,一个字节就可以表示很多内容。下面的图片展示了 MQTT 的固定头的格式,这个固定头只有 2 个字节:
第一个字节分成了高 4 位(4~7)和低 4 位(0~3);低 4 位是数据包标识位,其中的每一比特位又可以表示不同的含义;高 4 位是不同数据包类型的标识位。第二个字节表示数据包头部和消息体的字节共个数,其中最高位表示有没有第三字节存在,来和第二个字节一起表示字节共个数。如果有第三个字节,那它的最高位表示是否有第四个字节,来和第二个字节、第三个字节一起表示字节总个数。依此类推,还可能有第四个字节、第五个字节,不过这个表示可变头部和消息体的字节个数的部分,最多也只能到第五个字节,所以可以表示的最大数据包长度有 256MB。
比如,一个请求建立连接的 CONNECT 类型数据包,头部需要 14 个字节;发布消息的 PUBLISH 类型数据包头部只有 2~4 个字节。
另一方面,体现在消息的具体交互流程设计非常简单,所以 MQTT 的交互消息类型也非常少。
从表格可以看出,MQTT 3.1.1 版本一共定义了 14 种数据包的类型,在第一个字节的高 4 位中分别对应从 1 到 14 的数值。
时刻关注物联网设备低功耗需求的优化设计。
除了让协议足够轻量,MQTT 协议还很注重低功耗的优化设计,这主要体现在对能耗和通信次数的优化。
比如,MQTT 协议有一个 Keepalive 机制。它的作用是,在 Client 和 Broker 的连接中断时,让双方能及时发现,并重新建立 MQTT 连接,保证主题消息的可靠传输。这个机制工作的原理是:Client 和 Broker 都基于 Keepalive 确定的时间长度,来判断一段时间内是否有消息在双方之间传输。这个 Keepalive 时间长度是在 Client 建立连接时设置的,如果超出这个时间长度,双方没有收到新的数据包,那么就判定连接断开。
虽然 Keepalive 要求一段时间内必须有数据包传输,但实际情况是,Client 和 Broker 不可能时时刻刻都在传输主题消息,这要怎么办呢?
MQTT 协议的解决方案是,定义了 PINGREQ 和 PINGRESP 这两种消息类型。它们都没有可变头部和消息体,也就是说都只有 2 个字节大小。Client 和 Broker 通过分别发送 PINGREQ 和 PINGRESP 消息,就能够满足 Keepalive 机制的要求。我猜你也想到了,如果要一直这样“傻傻地”定期发送消息,那也太浪费电量和网络资源了。所以,如果在 Keepalive 时间长度内,Client 和 Broker 之间有数据传输,那么 Keepalive 机制也会将其计算在内,这样就不需要再通过发送 PINGREQ 和 PINGRESP 消息来判断了。
除了 Keepalive 机制,MQTT 5.0 中的重复主题特性也能帮助我们节省网络资源。Client 在重复发送一个主题的消息时,可以从第二次开始,将主题名长度设置为 0,这样 Broker 会自动按照上次的主题来处理消息。这种情况对传感器设备来说十分常见,所以这个特性在工作中很有实际意义。
针对物联网中多变的网络环境提供的多种服务质量等级。
MQTT 协议设计了 3 种不同的 QoS (Quality of Service,服务质量)级别。你可以根据场景灵活选择,在不同环境下保证通信是可靠的。
什么是 QoS?它是指通信双方关于消息传送可靠程度的协商。
QoS 0级别,消息只发送一次,消息可能丢失;
QoS 1级别 ,发送方会接收反馈,保证消息的送达,但是可能消息会重复。
QoS 2 级别,通过发送方和接收方的多次交互,保证消息有且只有一次。
可以看到,QoS 0 和 QoS 1 的流程相对比较简单;而 QoS 2 为了保证有且只有一次的可靠传输,流程相对复杂些。正常情况下,QoS 2 有 PUBLISH、PUBREC、PUBREL 和 PUBCOMP 4 次交互。至于“不正常的情况”,发送方就需要重复发送消息。比如一段时间内没有收到 PUBREC 消息,就需要再次发送 PUBLISH 消息。不过要注意,这时要把消息中的 “重复”标识设置为 1,以便接收方能正确处理。同样地,如果没有收到 PUBCOMP 消息,发送方就需要再次发送 PUBREL 消息。
支持在物联网应用中越来越被重视的数据安全。
说到安全传输,首先我们需要验证 Client 是否有权限接入 MQTT Broker。为了控制 Client 的接入,MQTT 提供了用户名 / 密码的机制。在建立连接过程中,它可以通过判断用户名和密码的正确性,来筛选有效连接请求。但是光靠这个机制,还不能保证网络通信过程中的数据安全。因为在明文传输的方式下,不止设备数据,甚至用户名和密码都可能被其他人从网络上截获而导致泄漏,于是其他人就可以伪装成合法的设备发送数据。所以,我们还需要通信加密技术的支持。MQTT 协议支持 SSL/TLS 加密通信方式。采用 SSL/TLS 加密之后,MQTT 将转换为 MQTTS。这有点类似于 HTTP 和 HTTPS 的关系。
体验 MQTT
使用hbmqtt简单体验MQTT
第一步是安装 hbmqtt,它是一个开源的基于 Python 语言的 MQTT Broker 软件,正好包括我们需要使用一些工具。跟其他选择相比,这个软件安装起来非常方便,因为它在 Python 的 PYPI 软件仓库中就有,所以你通过 pip 命令就可以安装。这也是选择使用它的主要原因。不过要注意的是,hbmqtt 是基于 Python3 实现的,因此这里使用的是 pip3 工具。
pip3 install hbmqtt
安装完成后,我们就可以使用 hbmqtt 中提供的 hbmqtt_sub 和 hbmqtt_pub 这两个命令行工具了。通过名字,你应该也可以看出 hbmqtt_sub 可以充当订阅者的角色;hbmqtt_pub 可以作为消息的发布者。
至于订阅者和发布者之间的经纪人,也就是 MQTT Broker,我们使用 Eclipse 免费开放的在线 Broker 服务。打开链接,你可以看到关于端口的介绍信息,加密和非加密方式都支持,而且还有基于 Websocket 的实现,这对基于前端网页的应用来说是非常有利的。
我们先使用 1883 端口的非加密方式,然后为消息传输确定一个主题(Topic)。主题确定了消息的类别,用于消息过滤。比如我们待会儿要测试的消息可以设为“/mqtt/test”。
接着,我们在电脑的终端界面输入下面的命令,就可以订阅这个主题消息:
hbmqtt_sub --url mqtt://test.mosquitto.org:1883 -t /mqtt/test
现在,我们启动另外一个终端界面,通过 hbmqtt_pub 发布一个 “/mqtt/test” 主题的消息:
hbmqtt_pub --url mqtt://test.mosquitto.org:1883 -t /mqtt/test -m Hello,World!
搭建自己的MQTT Broker,进行深度体验
使用VerneMQ作为MQTT Broker
第一步,使用docker安装VerneMQ。
docker run -p 1883:1883 -e "DOCKER_VERNEMQ_ACCEPT_EULA=yes" -e "DOCKER_VERNEMQ_ALLOW_ANONYMOUS=on" --name vernemqtt -d vernemq/vernemq
第二步,使用VSCode连接VerneMQ,安装VsMqtt插件。
MQTT 的客户端(Client)代码实现,可以使用Eclipse Paho 项目。