1. MQTT简介
MQTT
(MessageQueuingTelemetryTransport
,消息队列遥测传输协议)是一种基于发布/订阅(publish
/subscribe
)模式的“轻量级”通讯协议,该协议构建于TCP/IP
协议上,由IBM在1999年发布。
MQTT最大优点在于,作为一种低开销、低带宽占用的即时通讯协议,可以以极少的代码和有限的带宽,为连接远程设备提供实时可靠的消息服务。使其在物联网、小型设备、移动应用等方面有较广泛的应用。
TCP/IP
参考模型可以分为四层:应用层、传输层、网络层、链路层。TCP
和UDP
位于传输层,应用层常见的协议有HTTP
、FTP
、SSH
等。**MQTT**
协议运行于**TCP**
**之上,属于应用层协议,**因此只要是支持TCP/IP
协议栈的地方,都可以使用MQTT
。
2. MQTT特性
- 使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;
- 对负载内容屏蔽的消息传输;
- 使用
TCP/IP
提供网络连接; - 小型传输,开销很小(固定长度的头部是2字节),协议交换最小化,以降低网络流量;
- 使用
LastWill
和Testament
特性(最后遗嘱)通知有关各方客户端异常中断的机制。
3. MQTT术语
3.1 应用消息ApplicationMessage
-
MQTT
协议通过网络传输应用数据; - 应用消息通过
MQTT
传输时,它们有关联的服务质量(QoS
)和主题(Topic
)。
3.2 客户端Client
使用MQTT
的程序或设备。客户端总是通过网络连接到服务端。它可以:
- 发布应用消息给其它相关的客户端;
- 订阅以请求接受相关的应用消息;
- 取消订阅以移除接受应用消息的请求;
- 从服务端断开连接。
3.3 服务端Server
一个程序或设备,作为发送消息的客户端和请求订阅的客户端之间的中介。它可以:
- 服务端接受来自客户端的网络连接;
- 接受客户端发布的应用消息;
- 处理客户端的订阅和取消订阅请求;
- 转发应用消息给符合条件的已订阅客户端。
3.4 订阅Subscription
- 订阅包含一个主题过滤器(
TopicFilter
)和一个最大的服务质量(QoS
)等级; - 订阅与单个会话(
Session
)关联; - 会话可以包含多于一个的订阅。会话的每个订阅都有一个不同的主题过滤器。
3.5 主题名TopicName
- 附加在应用消息上的一个标签,服务端已知且与订阅匹配;
- 服务端发送应用消息的一个副本给每一个匹配的客户端订阅。
3.6 主题过滤器TopicFilter
- 订阅中包含的一个表达式,用于表示相关的一个或多个主题;
- 主题过滤器可以使用通配符。
3.7 会话Session
- 客户端和服务端之间的状态交互;
- 一些会话持续时长与网络连接一样,另一些可以在客户端和服务端的多个连续网络连接间扩展。
3.8 控制报文MQTTControlPacket
- 通过网络连接发送的信息数据包;
-
MQTT
规范定义了十四种不同类型的控制报文,其中一个(PUBLISH
报文)用于传输应用消息。
4. 什么是主题
MQTT
协议通过网络传输应用消息,应用消息通过MQTT
传输时,它们有关联的服务质量(QoS
)和主题(Topic
)。主题本质上是一个字符串,MQTT
协议规定主题是UTF-8
编码的字符串,这意味着,主题过滤器和主题名的比较可以通过比较编码后的UTF-8
字节或解码后的Unicode
字符。
4.1 主题名和主题过滤器
4.1.1 主题名
- 附加在应用消息上的一个标签,服务端已知且与订阅匹配。
- 服务端发送应用消息的一个副本给每一个匹配的客户端订阅。
4.1.2 主题过滤器
- 订阅中包含的一个表达式,用于表示相关的一个或多个主题。主题过滤器可以使用通配符。
- 如果订阅的主题过滤器与消息的主题名匹配,应用消息会被发送给每一个匹配的客户端订阅。
- 主题资源可以是管理员在服务端预先定义好的,也可以是服务端收到第一个订阅或使用那个主题名的应用消息时动态添加的。
- 服务端可以使用一个安全组件有选择地授权客户端使用某个主题资源。
4.2 主题和主题过滤器命名的规则
MQTT
协议中规定了主题是一段UTF-8
编码的字符串,主题需要满足以下规则:
- 所有的主题名和主题过滤器必须至少包含一个字符。
- 主题名和主题过滤器是大小写敏感的。
ACCOUNTS
和Accounts
是不同的主题名。 - 主题名和主题过滤器可以包含空格字符。
Accountspayable
是合法的主题名 - 主题名或主题过滤器以前置或后置斜杠
/
区分。/finance
和finance
是不同的。 - 只包含斜杠
/
的主题名或主题过滤器是合法的。 - 主题名和主题过滤器不能包含
null
字符(UnicodeU+0000
)。 - 主题名和主题过滤器是
UTF-8
编码字符串,除了不能超过UTF-8
编码字符串的长度限制之外,主题名或主题过滤器的层级数量没有其它限制。
4.3 主题层级
4.3.1 主题层级分隔符
斜杠(“/”U+002F
)用于分割主题的每个层级,为主题名提供一个分层结构。分隔符用于将结构化引入主题名。如果存在分隔符,它将主题名分割为多个主题层级,是消息主题层级设计中很重要的符号。比方说:aaa/bbb
、aaa/bbb/ccc
和aaa/bbb/ccc/ddd
这样的消息主题格式,是一个层层递进的关系,可通过多层通配符同时匹配两者,或者单层通配符只匹配一个。这在现实场景中,可以应用到:公司的部门层级推送、国家城市层级推送等包含层级关系的场景。MQTT
订阅报文包含一个主题过滤器(TopicFilter
)和一个最大的服务质量(QoS
)等级。订阅的主题过滤器可以包含特殊的通配符,允许客户端一次订阅多个主题。当客户端订阅指定的主题过滤器包含两种通配符时,主题层级分隔符就很有用了。主题层级分隔符可以出现在主题过滤器或主题名字的任何位置。相邻的主题层次分隔符表示一个零长度的主题层级。
主题过滤器中可以使用通配符,但是主题名不能使用通配符。单层通配符和多层通配符只能用于订阅(
subscribe
)消息而不能用于发布(publish
)消息,层级分隔符两种情况下均可使用。
4.3.2 多层通配符
井字符号(“#”U+0023
)是用于匹配主题中任意层级的通配符。多层通配符表示它的父级和任意数量的子层级。
例如,如果客户端订阅主题sport/tennis/player1/#
,它会收到使用下列主题名发布的消息:
sport/tennis/player1
sport/tennis/player1/ranking
sport/tennis/player1/score/wimbledon
因为多层通配符包括它自己的父级,所以sport/#
也匹配单独的sport
主题名,sport/tennis/player1/#
也可以匹配sport/tennis/player1
。
单独的多层通配符#
是有效的,它会收到所有的应用消息。
多层通配符必须单独指定,或者跟在主题层级分隔符后面。多层通配符必须是主题过滤器的最后一个字符。因此,sport/tennis#
和sport/tennis/#/ranking
都是无效的多层通配符。
4.3.3 单层通配符
加号(“+”U+002B
)是只能用于单个主题层级匹配的通配符。例如,sport/tennis/
+匹配sport/tennis/player1
和sport/tennis/player2
,但是不匹配sport/tennis/player1/ranking
。同时,由于单层通配符只能匹配一个层级,sport/
+不匹配sport
但是却匹配sport/
。
在主题过滤器的任意层级都可以使用单层通配符,包括第一个和最后一个层级,可以在主题过滤器中的多个层级中使用它,也可以和多层通配符一起使用,+
、+/tennis/#
、sport/+/player1
都有有效的。在使用单层通配符时,单层通配符占据过滤器的整个层级,sport+
是无效的。
4.3.4 以$
开头的主题
服务端不能将$
字符开头的主题名匹配通配符(#
或“+)开头的主题过滤器,订阅#
的客户端不会收到任何发布到以$
开头主题的消息,订阅+/monitor/Clients
的客户端也不会收到任何发布到$SYS/monitor/Clients
的消息。服务端应该阻止客户端使用这种主题名与其他客户端交换消息,客户端注意不能使用$
字符开头的主题。
服务端实现可以将$
开头的主题名用作其他目的。,例如$SYS/
被广泛用作包含服务器特定信息或控制接口的主题的前缀。订阅$SYS/#
的客户端会收到发布到以$SYS/
开头主题的消息,订阅$SYS/monitor/+
的客户端会收到发布到$SYS/monitor/Clients
主题的消息,如果客户端想同时接受以$SYS/
开头主题的消息和不以KaTeX parse error: Expected ‘EOF’, got ‘#’ at position 17: …头主题的消息,它需要同时订阅`#̲`和`SYS/#`。
4.3.5 举个例子
比如我们用传感器监视家里的卧室、客厅以及厨房的温度、湿度和空气质量,可以设计一下几个主题:
myhome/bedroom/temperature
myhome/bedroom/humidity
myhome/bedroom/airquality
myhome/livingroom/temperature
myhome/livingroom/humidity
myhome/livingroom/airquality
myhome/kitchen/temperature
myhome/kitchen/humidity
myhome/kitchen/airquality
当我们想获取卧室的所有数据时,可以订阅myhome/bedroom+
主题,当我们想获取三个房间的温度数据的时候,可以订阅myhome/+/temperature
主题,当我们想获取所有的数据的时候,可以订阅myhome/#
或者#
。
5. 消息服务质量
5.1 QoS0
,At most once,至多一次;
5.2 QoS1
,At least once,至少一次;
5.3 QoS 2
,Exactly once,确保只有一次。
-
PUBREC
消息
发布收到。是对QoS
级别为2
的PUBLISH
消息的响应。它是QoS
级别2
协议流的第二个消息。PUBREC
消息由服务器响应来自发布端的PUBLISH
消息,或订阅端响应来自服务器的PUBLISH
消息。发布端或服务器收到PUBREC
消息时,会响应PUBREL
消息。
-
PUBREL
消息
发布释放。是从发布端对PUBREC
的响应,或从服务器对订阅端PUBREC
消息的响应。这是QoS
级别2
协议流中第三个消息。当服务器从发布者收到PUBREL
消息时,服务器会将PUBLISH
消息发送到订阅端,并发送PUBCOMP
消息到发布端。当订阅端收到来自服务器的消息PUBREL
时,使得消息可用于应用程序并将PUBCOMP
消息发送到服务器。
-
PUBCOMP
消息
发布完成。是服务器对来自发布端的PUBREL
消息的响应,或订阅者对来自服务器的PUBREL
消息的响应。它是QoS
级别2
协议流程中的第四个也是最后一个消息。当发布端收到PUBCOMP
消息时,它会丢弃原始消息,因为它已经将消息发给了服务器。
5.4 特别注意
需要注意的是MQTT
发布与订阅操作中的QoS
代表了不同的含义:
- 发布时的
QoS
表示消息发送到MQTT
服务器使用的QoS
等级; - 订阅时的
QoS
表示MQTTBroker
向自己转发消息时可以使用的最大QoS
等级。
需要保障发送与订阅的**QoS**
一致,才能确保最终收到的消息是固定的**QoS**
等级,否则会出现消费降级的情况。例如:A
发送的消息QoS
为2
,B订阅的消息QoS
为1
,则最终接收到消息的QoS
为1
。
6. 保留消息和最后遗嘱
6.1 保留消息
RetainedMessages
。MQTT
中,无论是发布还是订阅都不会有任何触发事件。1个Topic
只有唯一的retain
消息,Broker
会保存每个Topic
的最后一条retain
消息。发布消息时把retain
设置为true
,即为保留信息。每个Client
订阅Topic
后会立即读取到retain
消息。如果需要删除retain
消息,可以发布一个空的retain
消息,因为每个新的retain
消息都会覆盖最后一个retain
消息。
6.2 最后遗嘱
LastWill&Testament
。MQTT
本身就是为信号不稳定的网络设计的,所以难免一些客户端会无故的和Broker
断开连接。当客户端连接到Broker
时,可以指定LWT
,Broker
会定期检测客户端是否有异常。当客户端异常掉线时,Broker
就往连接时指定的Topic
里推送当时指定的LWT
消息。