一些收藏

一文读懂物联网 MQTT 协议之基础特性篇

2021-07-26  本文已影响0人  老周聊架构

欢迎大家关注我的微信公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

一、前言

上个月有个读者问我物联网 MQTT 协议实战相关的问题,我说后面会搞,没想到不知不觉一个月了,太忙了,再怎么忙答应的事情还是要给读者一个交代,所以就有了此文。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

二、MQTT 协议概要

2.1 什么是 MQTT 协议

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅(publish/subscribe)模式的“轻量级”通讯协议,该协议构建于 TCP/IP 协议上,由 IBM 于 1999 年发明。MQTT 协议的主要特征是开放、简单、轻量级和易于实现,这些特征使得它适用于受约束的应用环境,如:

通过 MQTT 协议,目前已经扩展出了数十种 MQTT 服务器端程序,可以通过 PHP、Java、Python、C、C# 等语言向 MQTT 发送消息。由于开放源代码、耗电量小等特点,MQTT 非常适用于物联网领域,如传感器与服务器的通信、传感器信息采集等。

2.2 发布/订阅模式

发布/订阅模式并不是 MQTT 协议特有的模式,像我们很多消息中间件都有使用发布/订阅模式,这里你是不是想说,这不就是我们所说的观察者模式嘛,还真不是,这两个模式很容易混淆。观察者模式只有 观察者 + 被观察者两个角色,而发布/订阅模式还有一个经纪人 Broker;往更深层次的讲观察者和被观察者,是松耦合的关系,而发布者和订阅者,则完全不存在耦合。

在客户端/服务器模型中,客户端直接与服务器端点通信。而发布/订阅模式 pub/sub 就不一样了,发布/订阅模式会将发送消息的发布者 publisher 与接收消息的订阅者 subscribers 进行分离,publisher 与 subscribers 并不会直接通信,他们甚至都不清楚对方是否存在,他们之间的交流由第三方组件 broker 代理。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

pub/sub 最重要的方面是消息的发布者与接收者(订阅者)的解耦。这种解耦有几个维度:

总之,发布/订阅模式消除了传统客户端/服务器之间的直接通信,把通信这个操作交给了 broker 进行代理,并在空间、时间、同步三个维度上进行了解藕。

2.3 可扩展性

pub/sub 比传统的客户端/服务器模式有了更好的拓展,这是由于 broker 的高度并行化,并且是基于事件驱动的模式。可扩展性还体现在消息的缓存和消息的智能路由,还可以通过集群代理来实现数百万的连接,使用负载均衡器将负载分配到更多的单个服务器上,这就是 MQTT 的深度应用了。

2.4 消息过滤

很明显,broker 在 pub/sub 过程中起着举足轻重的作用。但是代理如何过滤所有消息,以便每个订阅者只接收感兴趣的消息?broker 有几个可以过滤的选项:

2.5 MQTT 与消息队列的区别

这里你又会说了,既然 MQTT 与主流的消息的队列都采用发布/订阅模式,那他们就是一样的。这里老周得再提一嘴,确实和消息队列很多相似的地方,但还有有些差异的,下面就来说道说道:

三、MQTT 重要概念

3.1 MQTT Client

publisher 和 subscriber 都属于 MQTT Client,之所以有发布者和订阅者这个概念,其实是一种相对的概念,就是指当前客户端是在发布消息还是在接收消息,发布和订阅的功能也可以由同一个 MQTT Client 实现。

MQTT 客户端是运行 MQTT 库并通过网络连接到 MQTT 代理的任何设备(从微控制器到成熟的服务器)。例如,MQTT 客户端可以是一个非常小的、资源受限的设备,它通过无线网络进行连接并具有一个最低限度的库。基本上,任何使用 TCP/IP 协议使用 MQTT 设备的都可以称之为 MQTT Client。MQTT 协议的客户端实现非常简单直接,易于实施是 MQTT 非常适合小型设备的原因之一。MQTT 客户端库可用于多种编程语言。例如,Android、Arduino、C、C++、C#、Go、iOS、Java、JavaScript 和 .NET。

3.2 MQTT Broker

与 MQTT Client 对应的就是 MQTT Broker,Broker 是任何发布/订阅协议的核心,根据实现的不同,代理可以处理多达数百万连接的 MQTT Client。

Broker 负责接收所有消息,过滤消息,确定是哪个Client 订阅了每条消息,并将消息发送给对应的 Client,Broker 还负责保存会话数据,这些数据包括订阅的和错过的消息。Broker 还负责客户端的身份验证和授权。

3.3 MQTT Connection

MQTT 协议基于 TCP/IP。客户端和代理都需要有一个 TCP/IP 协议支持。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

MQTT 连接始终位于一个客户端和代理之间。客户端从不直接相互连接。要发起连接,客户端向代理发送 CONNECT 消息。代理使用 CONNACK 消息和状态代码进行响应。建立连接后,代理将保持打开状态,直到客户端发送断开连接命令或连接中断。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

四、消息列表

4.1 CONNECT

为了创建连接,客户端向代理发送命令消息。如果此 CONNECT 消息格式错误(根据 MQTT 规范)或打开网络套接字和发送连接消息之间的时间过长,代理将关闭连接。

一个 MQTT 客户端发送一条 CONNECT 连接,这条 CONNECT 连接可能会包含下面这些信息:

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

我们将重点关注以下选项:

4.2 CONNACK

当 broker 收到 CONNECT 消息时,它有义务回复 CONNACK 消息进行响应。CONNACK 消息包括两部分内容:

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

4.3 PUBLISH

MQTT 客户端可以在连接到 broker 后立即发布消息,MQTT 使用的是基于 topic 主题的过滤。每条消息都必须包含一个主题,broker 可以使用该主题将消息转发给感兴趣的客户端。通常,每条消息都有一个负载(Payload),其中包含要以字节格式传输的数据。MQTT 是数据无关性的,也就是说数据是由发布者 - publisher 决定要发送的是 XML 、JSON 还是二进制数据、文本数据。

MQTT 中的 PUBLISH 消息有几个我们想要详细讨论的属性:

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

当客户端向 MQTT broker 发送消息进行发布时,broker 读取消息、确认消息(根据 QoS 级别)并处理消息。broker 的处理包括确定哪些客户端订阅了主题并将消息发送给他们。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

最初发布消息的客户端只关心将 PUBLISH 消息传递给 broker。一旦 broker 收到 PUBLISH 消息,broker 就有责任将消息传递给所有订阅者。发布客户端不会得到关于是否有人对发布的消息感兴趣或有多少客户端从 broker 收到消息的任何反馈。

4.4 Subscribe

client 会向 broker 发送 SUBSCRIBE 消息来接收有关感兴趣的 topic,这个 SUBSCRIBE 消息非常简单,它包含了一个唯一的数据包标识和一个订阅列表。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

4.5 Suback

为了确认每个订阅,broker 向客户端发送一个 SUBACK 确认消息。该消息包含原始 Subscribe 消息的数据包标识符(以明确标识该消息)和返回码列表。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

4.6 Unsubscribe

SUBSCRIBE 消息的对应是 UNSUBSCRIBE 消息。此消息删除 broker 上客户端的现有订阅。UNSUBSCRIBE 消息与 SUBSCRIBE 消息类似,具有数据包标识符和主题列表。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

4.7 Unsuback

为了确认取消订阅,broker 向客户端发送一个 UNSUBACK 确认消息。此消息仅包含原始 UNSUBSCRIBE 消息的数据包标识符(以明确标识该消息)。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

客户端收到来自 broker 的 UNSUBACK 后,可以认为 UNSUBSCRIBE 消息中的订阅被删除了。

五、Topics

前面我们说了很多 MQTT 协议的格式以及消息列表,这一节我们来说下 Topics 主题。主题在 MQTT 中很重要,因为我们写代码的时候往往都是需要先确认好 MQTT 的 Topics。

在 MQTT 中,主题一词是指 broker 用于为每个连接的客户端过滤消息的 UTF-8 字符串。主题由一个或多个主题级别组成。每个主题级别由正斜杠(主题级别分隔符)分隔。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

与消息队列相比,MQTT 主题非常轻量级。客户端在发布或订阅它之前不需要创建所需的主题。broker 接受每个有效主题而无需任何事先初始化。

5.1 通配符

当客户端订阅主题时,它可以订阅已发布消息的确切主题,也可以使用通配符同时订阅多个主题。通配符只能用于订阅主题,不能用于发布消息。有两种不同类型的通配符:单级和多级。

在这里插入图片描述

<figcaption style="line-height: inherit; margin: 0px; padding: 0px; margin-top: 10px; text-align: center; color: rgb(153, 153, 153); font-size: 0.7em;">在这里插入图片描述</figcaption>

**5.2 以 $ 开头的主题

通常,您可以根据需要命名 MQTT 主题。但是,有一个例外:以 开头的主题∗∗通常,您可以根据需要命名MQTT主题。但是,有一个例外:以 符号开头的主题具有不同的目的。当您将多级通配符作为主题 (#) 订阅时,这些主题不是订阅的一部分。$-symbol 主题保留用于 MQTT 代理的内部统计信息。客户端无法向这些主题发布消息。目前,此类主题尚无官方标准化。通常,$SYS/用于所有以下信息,但代理实现各不相同。MQTT GitHub wiki 中提供了对 $SYS-topics 的一项建议 。这里有些例子:

image.png

可以呀,看到了最后面。授人以鱼不如授人以渔,下面是一个关于 MQTT Version 3.1.1 的介绍,有些协议格式详细的可以前往查看。

https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718035

本文分基础特性篇与实战篇来讲,下一篇老周会带你搭建一个 MQTT 服务器,让其他厂商的设备接入进来,尽情期待~

上一篇 下一篇

猜你喜欢

热点阅读