目录

1、MQTT 介绍

2、MQTT 发布-订阅模式

(1)Broker 可拓展

(2)Broker 消息过滤

(3)MQTT 的主要特征是什么?

(4)MQTT 和消息队列的区别

3、MQTT 客户端、 Broker 、和连接建立(Connection Establishment)

(1)MQTT 客户端

(2)MQTT Broker

(3)MQTT 建立连接

4、MQTT 发布、订阅和取消订阅(Publish, Subscribe & Unsubscribe)

(1)Publish 发布 

(2)Subscribe 订阅 

(3)Unsubscribe 取消订阅 

5、MQTT Topics 和 MQTT 通配符

(1)MQTT Topics

(2)MQTT Wildcards (通配符)

(3)以 $ 开头的 MQTT 主题

(4)MQTT Best practices(主题使用建议)


1、MQTT 介绍

网络带宽非常宝贵,需要小的代码占用。

一种非常轻量级的二进制协议,由于它的包开销非常的小,与 HTTP 等协议相比,MQTT 在通过网络传输数据时表现更出色。该协议的另一个重要特征是 MQTT 非常容易在客户端实现。易用性是 MQTT 开发中的一个关键问题,这使得它非常适合资源有限的受限设备。// 轻量,易用

2、MQTT 发布-订阅模式

        文档连接。在客户机-服务器模型中,客户机直接与服务器通信。发布/订阅模型将发送消息的客户机(发布者)与接收消息的服务器(订阅者)分离开来。发布者和订阅者不直接进行通信。事实上,他们甚至都不知道对方的存在。它们之间的连接由第三个组件( broker )处理。broker 的工作是过滤所有传入的消息并将它们正确地分发给订阅者。// 很好描述了发布-订阅模式和客户端-服务器模式的区别

python mqtt订阅主题断连 mqtt订阅topic_MQTT

发布/订阅最重要的特征是消息的发布者与接收者(订阅者)的解耦。这种解耦有几个维度:空间解耦、时间解耦、通信同步解耦。

(1)Broker 可拓展

 broker 上的操作可以高度并行化,消息可以以事件驱动的方式处理。消息缓存和消息的智能路由通常是提高可伸缩性的决定性因素。然而,扩大到数百万个节点仍然是一个挑战。这样高级别的连接可以通过集群代理节点来实现,利用代理节点,使用负载平衡器将负载分布到更多的独立服务器上。// 有点类似网关的概念 + 负载均衡

(2)Broker 消息过滤

        基于主题的过滤(SUBJECT-BASED FILTERING):根据主题(subject / topic)分发消息。通常,主题是具有层次结构的字符串,允许基于有限数量的表达式进行过滤。

        基于内容的过滤(CONTENT-BASED FILTERING):broker 基于特定的内容筛选器语言筛选消息。broker 接收客户端订阅,并根据客户端查询过滤消息。这种方式的显著缺点是,客户端必须事先知道消息的内容,并且消息不能加密或随意变更。

        基于类型的过滤(TYPE-BASED FILTERING):当使用面向对象语言时,基于消息(事件)的类型或者类进行筛选是一种常见的方式。例如,订阅者可以监听类型为 Exception 或任何子类型的所有消息。

(3)MQTT 的主要特征是什么?

        空间上解耦发布者和订阅者:要发布或接收消息,发布者和订阅者只需要知道代理的主机名/IP和端口。

        时间上解耦:尽管大多数 MQTT 用例以接近实时的方式传递消息,但是如果需要,broker 可以为不在线的消费端存储消息。

因为大多数客户端程序(libraries)是异步工作的,它们基于回调或类似的模式,所以在等待消息或者发布消息时并不会阻塞任务。在某些用例中,需要进行消息同步,为了等待一个特定的消息,有些库提供了同步 API。但是,消息流通常还是异步的。

MQTT 使用基于主题的消息过滤方式。每条消息都包含一个主题(subject),broker 可以根据该主题决定订阅的客户端是否能获得此消息。

// 简单的说的就是发布订阅中的一些东西,无订阅情况怎么处理,主题怎么设计等

(4)MQTT 和消息队列的区别

// 主题必须订阅,消息必须有消费者。

在 MQTT 中,行为恰恰相反,订阅主题的每个订阅者都将获得消息。// 很多消息中间件都支持这个操作

MQTT 主题非常灵活,可以动态创建。// 消息队列这里指的仅仅只是简单的队列,从以上对比情况来看,有点类似于数据结构中的 Queue

3、MQTT 客户端、 Broker 、和连接建立(Connection Establishment)

(1)MQTT 客户端

        消息发布者和订阅者都是一个 MQTT 客户端(发布和订阅功能也可以在同一个MQTT Client中实现)MQTT 客户端可以是任何运行 MQTT Library (程序类库)并通过网络连接到 MQTT Broker 的设备(从微控制器到功能齐全的服务器)。基本上,任何通过 TCP/IP 堆栈使用 MQTT 的设备都可以称为 MQTT 客户端。MQTT 协议的客户端实现非常的直接和精简。易于实现是 MQTT 非常适合小型设备的原因之一。MQTT 客户端库可用于各种各样的编程语言。下边链接是基于 Java 的客户端实现:MQTT Java 客户端。更多实现版本,请查看 MQTT WIKI

(2)MQTT Broker

Broker 负责接收所有的消息、过滤消息、决定每条消息如何被订阅,并将消息发送到这些订阅的客户端。当客户端与 Broker 进行持久会话时,Broker 负责保存所有的会话数据,包括订阅信息和丢失的消息。同时,Broker 的还可以对客户端进行身份验证和授权。

// 高可用,易拓展、易监控

(3)MQTT 建立连接

        MQTT 是基于 TCP/IP 的协议。MQTT 的客户端和 Broker 都需要有一个 TCP/IP 堆栈。

python mqtt订阅主题断连 mqtt订阅topic_客户端_02

发起连接时,客户端向 Broker 发送一个 CONNECT 消息。代理响应一个 CONNACK 消息和一个状态码。一但连接建立后,Broker 将始终保持连接为打开状态,直到客户端发送断开连接命令或者连接被迫中断。

python mqtt订阅主题断连 mqtt订阅topic_标识符_03

通过 NAT 模式建立 MQTT 连接

        大多数情况下,MQTT 客户端位于私有网络中,通过网络地址转换(NAT)与公有网络进行通信。前边已经提到,MQTT 客户端通过向 Broker 发送 CONNECT 消息来建立连接。由于 Broker 有一个公共地址,并连接保持打开状态,从而可以允许双向的发送和接收消息(在初始化 CONNECT 之后),因此客户端使用 NAT 模式是完全没有问题的。

客户端使用 CONNECT 消息发起连接

        建立连接时,如果客户端发送的 CONNECT 消息格式不正确(根据 MQTT 规范),或者打开网络套接字后,客户端发送连接消息的时间过长,Broker 将关闭这个连接。这种方式可以阻止有问题的客户端降低 Broker 的速度。一个 MQTT 3 客户端发送的连接消息内容如下:

python mqtt订阅主题断连 mqtt订阅topic_标识符_04

        MQTT 规范的更过信息,可以查阅 MQTT 3.1.1 specification.

ClientId:客户端唯一标识。Broker 使用 ClientId 来标识客户端和客户端的当前状态。在 MQTT 3.1.1 版本中,如果不需要 Broker 保存客户端状态,则可以发送空的 ClientId 。空的 ClientId 会导致没有任何状态的连接。在这种情况下,清除会话标志(Clean Session)必须设置为 true,否则 Broker 将拒绝连接。

Clean Session:清除会话标志告诉 Broker,客户端是否希望建立持久会话。在持久会话中(CleanSession = false),Broker 存储客户端的所有订阅信息,以及所有的未处理消息(客户端的订阅级别为 QoS 1 或者 2)。如果是非持久会话(CleanSession = true),Broker 不会为客户端存储任何东西,并会清除之前任何持久会话中的所有信息。

Username/Password:用户客户端身份验证和授权。如果对用户名和密码不进行加密,将使用明文对这些信息进行传输。推荐使用加密方式传输认证信息,比如使用 SSL 证书。

Will Message:遗嘱消息是 MQTT LWT 特征(Last Will and Testament)的一部分。 当客户端异常中断时,遗嘱消息将会发送到其他的客户端。客户端建立连接时,可以在 CONNECT 消息中以 MQTT 消息和主题的形式向 Broker 提供遗嘱消息。如果客户端异常中断,Broker 代表客户端发送 LWT 消息。关于更多 LWT 消息请点击这里。// 遗嘱消息有什么用?

KeepAlive:以秒为单位的时间间隔。此时间间隔定义了 Broker 和客户端在不发送消息的情况下可以保持连接的最长时间。客户端向代理发送定期的 PING 请求消息。代理响应一个 PING 的回复消息。通过这种方式,允许双方可以确定另一方是否仍然可用。关于 KeepAlive 的更多信息,请点击这里

        一般情况下,以上的信息就是 MQTT 3.1.1 客户端连接到 MQTT Broker 所需的所有信息。单个客户端通常有额外的选项,你也可以对这些选项进行配置。

Broker 响应一个 CONNACK 消息

        当 Broker 接收到 CONNECT 消息时,它有义务用 CONNACK 消息响应。

        CONNACK 消息包含两个数据项:

  1. 当前会话标志(The session present flag)
  2. 连接返回码(A connect return code)

Session Present Flag:该标志告诉客户端,Broker 是否已经持有该客户端的持久会话。当客户端连接时,Clean Session 标志设置为 true,那么 Session Present flag 标志总是返回 false,因为没有可用的会话。如果客户端连接时,Clean Session 设置为 false,有两种可能:如果 Broker 已经存储了该 clientId 的会话信息,那么 Session Present flag 标志返回为 true。如果 Broker 不持有该 clientId 的任何会话信息,那么 Session Present flag 标志为 false。该标志是在 MQTT 3.1.1 中添加的,用来帮助客户端确认它们是否需要订阅主题,或者订阅的主题是否仍然存储在持久会话当中。// 判断一个 ClientId 是否已经拥有持久会话

Connect Return Code:CONNACK 消息中的连接确认标志。该标志包含一个返回码,告诉客户端连接是否成功。

python mqtt订阅主题断连 mqtt订阅topic_多级_05

        下边是返回的确认码表:

返回码

描述

0

接受连接

1

拒绝连接,不可接受的协议版本

2

拒绝连接,标识符被拒绝

3

拒绝连接,服务器不可用

4

拒绝连接,用户名或密码错误

5

拒绝连接,未授权

4、MQTT 发布、订阅和取消订阅(Publish, Subscribe & Unsubscribe)

(1)Publish 发布 

        每条消息必须包含一个主题,Broker 可以使用该主题将消息转发给订阅该主题的客户端。通常,每条消息都有一个有效负载,其中包含以字节格式传输的数据。MQTT 对数据是无感知的,所以客户端的用例决定了 MQTT 有效负载的结构。发送客户端(发布者)决定它是要发送二进制数据、文本数据,还是完整的 XML 或 json 数据。

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

python mqtt订阅主题断连 mqtt订阅topic_客户端_06

Packet Identifier:包标识符唯一地标识消息。包标识符只与大于零的 QoS 级别相关。客户端或 Broker 负责设置这个内部 MQTT 标识符。
        Topic Name:简单的字符串格式的主题名称。
        QoS:此数字表示消息的服务质量级别( QoS )。Qos 有三个级别:0,1,2;每个级别对数据消费提供不同的保证。// 最多一次,最少一次,仅有一次
        Retain Flag:此标志定义 Broker 是否将该消息保存为指定主题的最后一个已知的正确值。当新的客户端订阅某个主题时,它们将收到该主题上保留的最后一条消息。
        Payload:消息的实际内容。 MQTT 是消息无感知的,它可以发送任何数据。
        DUP flag:消息重传标志。该标志表示该消息为副本,由于预期的收件人(客户端或 Broker )不确认原始消息而被重发。该标志只与 QoS 大于 0 有关。一般情况下,重传机制由 MQTT 客户端或 Broker 实现。

        当消息发送给 MQTT Broker 后,Broker 负责处理这些消息:

python mqtt订阅主题断连 mqtt订阅topic_python mqtt订阅主题断连_07

(2)Subscribe 订阅 

        如果要接收特定主题的消息,客户端需要向 MQTT Broker 发送 SUBSCRIBE 消息。订阅消息非常简单,它包含一个惟一的包标识符和一个订阅列表。

python mqtt订阅主题断连 mqtt订阅topic_客户端_08

        Packet Identifier:消息的唯一标识符
        List of Subscriptions:订阅列表可以包含一个客户端的多个订阅信息。每个订阅由一个主题和一个 QoS 级别组成。订阅消息中的主题可以包含通配符,从而可以订阅一个主题模式而不是一个特定的主题。如果一个客户端有重叠的订阅,Broker 将为该主题交付具有最高 QoS 级别的消息。// 如果订阅有重复,使用订阅中 QoS 级别最高的那个订阅进行交付。

Suback 订阅确认

        为了确认每个订阅,Broker 会向客户端发送 SUBACK 确认消息。确认消息包含原始订阅消息的包标识符(以清楚地标识消息)和一个返回码列表。

python mqtt订阅主题断连 mqtt订阅topic_标识符_09

 Packet Identifier:消息的唯一标识符。
        Return Code:Broker 为它在 SUBSCRIBE 消息中接收到的每个主题/QoS-pair 发送一个返回码。例如,如果 SUBSCRIBE 消息有五个订阅,那么 SUBACK 消息包含五个返回码。返回码用来确认每个主题,并指示 Broker 授予的 QoS 级别。如果 Broker 拒绝订阅,则 SUBACK 消息会返回包含针对该特定主题的失败返回码。例如,如果客户端订阅主题的权限不足,或者主题格式不正确等。

返回码

说明

0

Success - Maximum QoS 0

1

Success - Maximum QoS 1

2

Success - Maximum QoS 2

128

Failure

python mqtt订阅主题断连 mqtt订阅topic_客户端_10

        当客户端成功发送 SUBSCRIBE 消息并接收 SUBACK 消息后,它将获得与订阅中的主题相匹配的每个已发布消息。

(3)Unsubscribe 取消订阅 

        与 SUBSCRIBE 消息对应的是 UNSUBSCRIBE 消息。此消息删除 Broker 上客户端的现有订阅。UNSUBSCRIBE 消息和 SUBSCRIBE 消息相似,也具有一个包标识符和一个主题列表。

python mqtt订阅主题断连 mqtt订阅topic_多级_11

List of Topic:主题列表可以包含客户端要取消订阅的多个主题。只需要发送主题(没有 QoS)。Broker 取消订阅主题,而不需要管它最初订阅的 QoS 级别是什么。

Unsuback 取消订阅确定

        为了确认退订,Broker 向客户端发送一个 UNSUBACK 确认消息。该确认消息仅包含原始 UNSUBSCRIBE 消息的包标识符。

python mqtt订阅主题断连 mqtt订阅topic_标识符_12

        在从 Broker 接收到 UNSUBACK 后,客户端可以认为 UNSUBSCRIBE 消息中的订阅已被删除。

python mqtt订阅主题断连 mqtt订阅topic_标识符_13

5、MQTT Topics 和 MQTT 通配符

(1)MQTT Topics

        MQTT 主题由一个或多个主题级别组成。每个主题级别由正斜杠(主题级别分隔符)分隔。

python mqtt订阅主题断连 mqtt订阅topic_MQTT_14

// 主题和消息一起发布

        注意,每个主题必须至少包含 1 个字符,并且主题字符串允许使用空格。主题内容区分大小写。例如,myhome/temperature 和 MyHome/Temperature 是两个不同的主题。此外,正斜杠本身也是一个有效的主题。

(2)MQTT Wildcards (通配符)

        通配符只能用于订阅主题,不能用于发布消息。MQTT 有两种不同的通配符:单层通配符和多层通配符。

Single Level: +

        顾名思义,单级通配符替代一个主题级别。“+” 号表示主题中的单级通配符。

python mqtt订阅主题断连 mqtt订阅topic_python mqtt订阅主题断连_15

Multi Level: #

多级通配符必须放在主题的最后一个字符并且在前面加上正斜杠。

python mqtt订阅主题断连 mqtt订阅topic_python mqtt订阅主题断连_16

        当客户端订阅具有多级通配符的主题时,它将接收以通配符之前的样式开始的主题的所有消息,无论该主题有多长或多深。如果只指定多级通配符作为主题( # ),那么将接收发送到 MQTT Broker 的所有消息。如果期望高吞吐量,那么仅使用多级通配符订阅是一种与期望相悖的方式。

(3)以 $ 开头的 MQTT 主题

// 以 $ 开头的 MQTT 主题,属于系统主题,与常规主题不同,客户端不能在该主题上发布消息。

(4)MQTT Best practices(主题使用建议)

不要使用前斜杠。例如,/myhome/groundfloor/livingroom,虽然 MQTT 允许这样使用,但是前斜杠引入了一个不必要的主题级别,表示前面有一个零字符。零并没有带来任何好处,而且造成混淆。

不要在主题中使用空格。空间是每个程序员的天敌。当事情没有按照应有的方向发展时,空格会使阅读和调试主题变得更加困难。与前斜杠一样,虽然 MQTT 允许使用这些东西,但并不意味着应该去使用它。UTF-8有许多不同的空白类型,应该尽量避免使用这种不常见的字符。

保持 MQTT 主题的简短和简洁。当涉及到小型设备时,每个字节计数和主题长度都会有很大的影响。

只使用 ASCII 字符,避免不可打印字符。因为 non-ASCII UTF-8 字符经常显示不正确,所以很难发现与字符集相关的拼写错误或问题。

在主题中嵌入唯一标识符或客户端 ID

不要使用 # 订阅所有主题。虽然有时候会需要订阅 Broker 传输的所有消息,比如,需要将所有的消息持久化到数据库中。但是,不要使用多级通配符方式来订阅 Broker 中的所有消息。通过情况下,一个客户端无法一次性处理所有数据,如果需要使用 Broker 传输的所有消息,建议在 MQTT Broker 中实现拓展。

不要忘记可扩展性。

使用明确的主题,不要使用笼统的主题。对所有消息使用单一的主题是一种非常不好的方式。对不同的消息使用不同的主题命名,还可以使用到其他 MQTT 的特性,比如保留消息。