1. MQTT 5.0 介绍
MQTT 协议 因为其轻量、灵活等特点成为了当今世界上最受欢迎的物联网协议,它已经广泛应用于车联网、智能家居、物流、即时聊天应用和移动消息推送等领域,连接了数以亿计的设备,并且每时每刻都有无数设备开始使用和接入 MQTT 协议。MQTT 协议为这些设备提供了稳定、可靠的通信基础,这些设备庞大的接入数量也向 MQTT 协议规范提出了挑战, MQTT 5.0 的诞生便是为了更好地满足这一需求。
MQTT(消息队列遥测传输)最初由 IBM 于上世纪 90 年代晚期发明。它最初的用途是将石油管道上的传感器与卫星相链接,所以 MQTT 从则搏局诞生之初就是专为受限设备和低带宽、高延迟或不可靠的网络而设计,它使用了发布订阅模型,在空间和时间上解耦了消息的发送者与接收者,并且基于 TCP/IP 提供稳定可靠的网络连接,拥有非常轻量的报头以减少传输开销,支持可靠消息传输,可以说天生就满足了物联网场景的各种需求。在 MQTT 3.1.1 发布并成为 OASIS 标准的四年后,MQTT 5.0 正式发布,银雹这是一次重大的改进和升级,它的目的不仅仅是满足现阶段的行业需求,更是为行业未来的发展变化做了充足的准备。2019 年 3 月,MQTT 5.0 成为了新的 OASIS 标准。
面对迅速增长的设备数量和层出不穷的需求,OASIS MQTT 技术委员会需要从繁杂的需求中提取出通用部分,将其纳入标准规范,并且尽可能不增加开销或降低易用性,在不增加不必要的复杂性的前提下提高性能和易用性。
最终,OASIS MQTT 技术委员会为 MQTT 5.0 添加了大量的全新功能与特性,5.0 成为 MQTT 有史以来变化最大的一个孙让版本。在这里,我们将列举一些比较重要的特性:
完整的新属性列表包含在协议标准的附录C,您可以访问以下网址了解详情: http://docs.oasis-open.org/mqtt/mqtt/v5.0/cs02/mqtt-v5.0-cs02.html#AppendixC 。
随着各 MQTT 服务器 厂商不断加入 MQTT 5.0 的支持阵营(例如 EMQ 在 2018 年 9 月就已经完整支持了 MQTT 5.0 协议),整个行业生态逐步迁移至 MQTT 5.0 已经成为大的趋势,MQTT 5.0 也将是未来绝大多数物联网企业的首选。我们也希望用户能够尽早拥抱 MQTT 5.0 并且享受到它带来的便利,这也是这篇文章的目的。如果您已经对 MQTT 5.0 产生了一些兴趣,但还想了解更多,您可以尝试阅读以下文章,我们将以通俗易懂的方式为您介绍 MQTT 5.0 的重要特性:
2. MQTT 基本认知
物联网 (internet of thing) ,表示的是可以把一些带某些传感器的设备(终端),接入到互联网的行为。
通过互联网连接这些设备,这些设备就能够互相协作。
而 MQTT 就是这些设备之间数据通信的一个基于 TCP/IP 的协议。
每个终端都和实现了 MQTT 协议的代理/服务器相连。
通过 published MQTT 代理服务器的某个 主题 发送数据。
通过 subscription 从 MQTT 代理服务器获取自己订阅的 主题 数据。
MQTT 协议是一种轻量级的、灵活的网络协议。并且非常适合 IOT 的场景。
大多数开发人员已经熟悉了 HTTP WEB 协议。那么为什么不让 IOT 设置链接到 WEB 服务?
设备可以采用 HTTP 请求的形式发送数据,并采用 HTTP 响应的形式从服务器获取数据,接受更新。
因为对于 IOT 的设备来说,这种 主动请求--> 被动等待应答的 数据传输模型存在严重的局限性:
那么,MQTT 为什么如此轻便且灵活?MQTT 协议的一个关键的特性是 发布/订阅模型 。它将数据的发布者和接受者分离。
一个设备终端既可以是数据的发布者 (published) 也可以是数据的订阅者 (subscription) 。
一个设备如果要发布数据,只需要往代理服务器中 相应的主题发布数据内容即可。
一个设备如果需要接受到数据,只需要在代理服务器中, 提前订阅自己需要关注的主题即可。
MQTT 最基本的体验,就是使用 mosquitto 。
Mosquitto是一款实现了 MQTT v3.1 协议的开源消息代理软件,提供轻量级的,支持发布/订阅的的消息推送模式,使设备对设备之间的短消息通信简单易用。
它可以理解成一个 MQTT 的代理服务器。
基本步骤如下:
安装成功截图
使用 brew services start mosquitto 启动 MQTT 服务
运行截图
然后再打开另外两个终端窗口,模拟两个IOT设备。A 订阅 MQTT 服务。B 向 MQTT 的服务发送数据。
A订阅当前MQTT的某个服务。
B向 MQTT 服务器发布(published) 数据。
然后,我们就可以在A控制台里看到由 B 通过 MQTT 服务发送的数据了。
基本流程图
控制台 A 向 MQTT 服务器订阅 dw/demo 服务,并被动的等待 MQTT 服务器返回数据。
控制台 B 主动的向 MQTT 服务器的 dw/demo 服务发送 published 数据,之后。服务器会主动向事先订阅了 dw/demo 的终端分发此消息。
MQTT 是一种链接协议,它指定了如何组织数据字节并通过 TCP/IP 网络传输它们。但实际上,开发人员并不需要链接这个链接协议的具体细节。我们只需要知道,每条消息都有一个命令和数据有效负载。该命令定义消息类型(比如 CONNECT 消息或者 SUB SCRIBE 消息)。所有的 MQTT 库和工具都提供了直接处理这些消息的基本方法,并且能自动填充一些必要的字段(在数据包的对应字节填充),比如消息和客户端 ID。
首先客户端发送一条 CONNECT消息 来链接代理。CONNECT 消息要求建立从客户端到代理服务器的链接。
CONNECT 命令的基本参数
当客户端向代理服务器发送一条 CONNECT 命令之后,服务器会调用 CONNACK 命令,告知服务链接的状态。
CONNACK 命令的基本参数
当客户端和服务器建立连接之后,客户端就可以向服务器订阅某些主题的。(发送一条或多条 SUBSCRIBE消息 )。
表明当服务器接受到其他终端推送的此主题数据时,服务器会默认发送给它。
SUBSCRIBE 参数列表
当客户端成功的向服务器订阅某个主题之后,服务器会返回一条 SUBACK 的消息,其中包含一个或者多个 returnCode 参数。
SUBACK消息参数
returnCode : 值 0 - 2 ,表示成功订阅,并返回这个订阅消息的 QOS。值 128 : 订阅失败。
既然客户端可以向服务器订阅某个主题,当然也可以取消订阅。
与 SUBSCRIBE 订阅命令相反的命令是 UNSUBSCRIBE 取消订阅命令。
此命令非常简单。只有一个topic(主题)参数。
上面讲的是订阅,订阅是需要有消息从服务器发送过来的。但是服务器本身基本不产生数据,那数据从何而来呢?
通过另外一个客户端执行 PUBLISH 命令,往代理服务器发送数据。并最终通过代理服务器将数据传递给订阅了此服务的客户端。
PUBLISH 消息参数
对于 MQTT 的一张基本理解图
基本流程图:
最后总结
参考资料: 初识 MQTT
3. EMQ X 规则引擎系列 (八)桥接消息到 MQTT Broker
桥接是一种连接多猜毕局个 EMQ X 或者其他 MQTT 消息中间件的方式。不同于集群,工作在桥接模式下的节点之间不会复制主题树和路由表。桥接模式所做的是:
工作在桥接模式下和工作在集群模式下有不同的应用场景,桥接可以完成一些单纯使用集群无法实现的功能:
在具体应用中,一个桥接的发起节点可以被近似的看作一个远程节点的客户端。
该场景需要将 EMQ X 指定主题下且满足条件的消息桥接到 EMQ X 或其他 MQTT Broker。
该场景下设备端上报信息如下:穗让
当上报数据发动机转速数值大于 8000 时,将该条信息部分数据桥接到指定服务器。
为了避免与本地的 emqx 出现端口冲突的情况,这里临时修改一下 mosquitto 的本地端口号。
打开 EMQ X Dashboard,进入左侧菜单的 资源 页面,点击 新建 按钮,键入 Mosquitto 服务器信息进行资源创建。
EMQ X 集群中节点所在网络环境可能互不相通,资源创建成功后点击列表中 状态按钮 ,查看各个节点资源连接状况,如果节点上资源不可用,请检查配置是否正确、网络连通性,并点击 重连 按钮手动重连。
进入左侧菜单的 规则 页面,点击 新建 按钮,进行规则创数吵建。这里选择触发事件 消息发布 ,在消息发布时触发该规则进行数据处理。
选定触发事件后,我们可在界面上看到可选字段及示例 SQL:
规则引擎使用 SQL 语句处理规则条件,该业务中我们需要将 payload 中所有字段单独选择出来,使用 payload.fieldName 格式进行选择,还需要消息上下文的 topic 、 qos 、 id 信息,当前 SQL 如下:
使用 SQL 语句 WHERE 字句进行条件筛选,该业务中我们需要定义两个条件:
组合上一步骤得到 SQL 如下:
借助 SQL 测试功能,我们可以实时查看当前 SQL 处理后的数据输出,该功能需要我们指定 payload 等模拟原始数据。
payload 数据如下,注意更改 tachometer 数值大小,以满足 SQL 条件:
点击 SQL 测试 切换按钮,更改 topic 与 payload 为场景中的信息,点击 测试 按钮查看数据输出:
测试输出数据为:
测试输出与预期相符,我们可以进行后续步骤。
SQL 条件输入输出无误后,我们继续添加相应动作,配置写入 SQL 语句,将筛选结果桥接到 Mosquitto。
点击响应动作中的 添加 按钮,选择 桥接数据到 MQTT Broker 动作,选取刚刚选定的资源。
我们成功创建了一条规则,包含一个处理动作,动作期望效果如下:
切换到 工具 --> Websocket 页面,使用任意信息客户端连接到 EMQ X,连接成功后在 消息 卡片发送如下信息:
点击 发送 按钮,发送成功后查看得到当前规则已命中统计值为 1。
命令行中查看数据表记录得到数据如下:
至此,我们通过规则引擎实现了使用规则引擎桥接消息到 MQTT Broker 的业务开发。
准备另外一台 emqx 节点,启动两台 emqx。
打开 EMQ X Dashboard,进入左侧菜单的 资源 页面,点击 新建 按钮,键入 EMQ X 服务器信息进行资源创建。
EMQ X 集群中节点所在网络环境可能互不相通,资源创建成功后点击列表中 状态按钮 ,查看各个节点资源连接状况,如果节点上资源不可用,请检查配置是否正确、网络连通性,并点击 重连 按钮手动重连。
进入左侧菜单的 规则 页面,点击 新建 按钮,进行规则创建。这里选择触发事件 消息发布 ,在消息发布时触发该规则进行数据处理。
选定触发事件后,我们可在界面上看到可选字段及示例 SQL:
规则引擎使用 SQL 语句处理规则条件,该业务中我们需要将 payload 中所有字段单独选择出来,使用 payload.fieldName 格式进行选择,还需要消息上下文的 topic 、 qos 、 id 信息,当前 SQL 如下:
使用 SQL 语句 WHERE 字句进行条件筛选,该业务中我们需要定义两个条件:
组合上一步骤得到 SQL 如下:
借助 SQL 测试功能,我们可以实时查看当前 SQL 处理后的数据输出,该功能需要我们指定 payload 等模拟原始数据。
payload 数据如下,注意更改 tachometer 数值大小,以满足 SQL 条件:
点击 SQL 测试 切换按钮,更改 topic 与 payload 为场景中的信息,点击 测试 按钮查看数据输出:
测试输出数据为:
测试输出与预期相符,我们可以进行后续步骤。
SQL 条件输入输出无误后,我们继续添加相应动作,配置写入 SQL 语句,将筛选结果桥接到另一个 EMQ X。
点击响应动作中的 添加 按钮,选择 桥接数据到 MQTT Broker 动作,选取刚刚选定的资源。
我们成功创建了一条规则,包含一个处理动作,动作期望效果如下:
切换到 工具 --> Websocket 页面,使用任意信息客户端连接到 EMQ X,连接成功后在 消息 卡片发送如下信息:
点击 发送 按钮,发送成功后查看得到当前规则已命中统计值为 1。
使用命令行中查看数据表记录得到数据如下:
至此,我们通过规则引擎实现了使用规则引擎桥接消息的业务开发。
4. 3. MQTT简要介绍
——
[1.MQTT项目工程](https://github.com/LiamBindle/MQTT-C)
[2.MQTT API说明文档](https://liambindle.ca/MQTT-C/group__api.html)
[3.MQTT协议中文版](https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introction.html)
MQTT是一个客户端服务端架构的发布/订阅模式的消息传输协议。它的设计思想是轻巧、开放、简单、规范,易于实现。这些特点使得它对很多场景来说都是很好的选择,特别是对于受限的环境如机器与机器的通信(M2M)以及物联网环境(IoT)。
MQTT协议通过交换预定义的MQTT控制报文来通信。
报文格式: 固定包头+可变包头+payload。
固定包头: 由两个字节组成
byte1 高四位表示控制报文的类型,低四位表示控制报文类型的标志位。
byte2表示剩余长度,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。
可变包头:
某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标(Packet Identifier)字段存在于在多个类型的报文里。
有效载荷:
对于PUBLISH来说有效载荷就是应用消息。
——1. 网络建立连接后,客户端发送的第一个报文必须是CONNECT报文
——2. 客户端只能发送一次CONNECT 报文,发送两次会当作协议违规处理,并断开连接。
——3. 有效载荷包括:客户端唯一标识符,will主题,will消息,用户名和密码。
——4.可变包头:协议名(protocol name)+协议级别(protocol level)+连接标志(connect flags)+保持连接(keep alive)。
——5. 协议名: MQTT的UTF-8编码的字符串。(MSB+LSB +MQTT 六个字节)
——6. 协议级别:一个字节
——7. 连接标志:一个字节,包含一些用于指定MQTT连接行为的参数。同时还指出有效载荷中的字段是否存在。服务端必须验证CONNECT控制报文的保留标志位(第0位)是否为0,如果不为0必须断开客户端连接。
——8. 清理会话:byte8 的bit1位标志。
这个二进制位指定了会话状态的处理方式。客户端和服务端可以保存会话状态,以支持跨网络连接的可靠消息传输。这个标志位用于控
制会话状态的生存时间。
标志设置为0:服务端必须基于当前会话(使用客户端标识符识别)的状态恢复与客户端的通信。
标志设置为1:客户端和服务端必须丢弃之前的任何会话并开始一个新的会话。会话仅持续和网络连接同样长的时间。
——9. 遗嘱标志 WILL FLAG: byte8的bit2位标志。
遗嘱标志(Will Flag)被设置为1,表示如果连接请求被接受了,遗嘱(Will Message)消息必须被存储在服务端并且与这个网络连接关联。之后网络连接关闭时,服务端必须发布这个遗嘱消息,除非服务端收到DISCONNECT报文时删除了这个遗嘱消息。
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理 的时间取决于应用的类型和通信基础设施。
CONNACK报文没有有效载荷。
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
固定包头:
注意byte1的bit3为重发标志DUP。
如果DUP标志被设置为0,表示这是客户端或服务端第一次请求发送这个PUBLISH报文。如果DUP标志被设置为1,表示这可能是一个早前报文请求的重发。
服务端发送PUBLISH报文给订阅者时,收到(入站)的PUBLISH报文的DUP标志的值不会被传播。发送(出站)的PUBLISH报文与收到(入站)的PUBLISH报文中的DUP标志是独立设置的,它的值必须单独的根据发送(出站)的PUBLISH报文是否是一个重发来确定。
可变包头:
主题名 :topic name
报文标识符 :packet identitfier。
有效载荷 :有效载荷包含将被发布的应用消息。数据的内容和格式是应用特定的。有效载荷的长度这样计算:用固定报头中的剩余长度字段的值减去可变报头的长度。包含零长度有效载荷的PUBLISH报文是合法的。
响应:PUBLISH报文的接收者必须按照根据PUBLISH报文中的QoS等级发送响应。
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
PUBACK报文没有有效载荷。
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
PUBREC报文没有有效载荷。
PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
PUBREL报文没有有效载荷。
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
PUBCOMP报文没有有效载荷。
客户端向服务端发送 SUBSCRIBE 报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
有效载荷:
SUBSCRIBE报文的有效载荷包含了一个主题过滤器列表,它们表示客户端想要订阅的主题。SUBSCRIBE报文的有效载荷必须包含至少一对主题过滤器 和 QoS等级字段组合。没有有效载荷的SUBSCRIBE报文是违反协议的。
响应:
服务端收到客户端发送的一个SUBSCRIBE报文时,必须使用SUBACK报文响应,SUBACK报文必须和等待确认的SUBSCRIBE报文有相同的报文标识符。
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
有效载荷:
有效载荷包含一个返回码清单。每个返回码对应等待确认的SUBSCRIBE报文中的一个主题过滤器。返回码的顺序必须和SUBSCRIBE报文中主题过滤器的顺序相同。
客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。
有效载荷 :
UNSUBSCRIBE报文的有效载荷包含客户端想要取消订阅的主题过滤器列表。
UNSUBSCRIBE报文中的主题过滤器必须是连续打包的、按照定义的UTF-8编码字符串
UNSUBSCRIBE报文的有效载荷必须至少包含一个消息过滤器。没有有效载荷的UNSUBSCRIBE报文是违反协议的。
响应:
UNSUBSCRIBE报文提供的主题过滤器(无论是否包含通配符)必须与服务端持有的这个客 户端的当前主题过滤器集合逐个字符比较。如果有任何过滤器完全匹配,那么它(服务端)自己的订阅将被删除,否则不会有进一步的处理。
如果服务端删除了一个订阅:
——它必须停止分发任何新消息给这个客户端 []。
——它必须完成分发任何已经开始往客户端发送的QoS 1和QoS 2的消息 []。
——它可以继续发送任何现存的准备分发给客户端的缓存消息。
服务端必须发送UNSUBACK报文响应客户端的UNSUBSCRIBE请求。UNSUBACK报文必须包含和UNSUBSCRIBE报文相同的报文标识符 。即使没有删除任何主题订阅,服务端也必须发送一个UNSUBACK响应 。
如果服务端收到包含多个主题过滤器的UNSUBSCRIBE报文,它必须如同收到了一系列的多个UNSUBSCRIBE报文一样处理那个报文,除了将它们的响应合并到一个单独的UNSUBACK报文外。
服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
UNSUBACK报文没有有效载荷。
客户端发送PINGREQ报文给服务端的。用于:
1. 在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
2. 请求服务端发送 响应确认它还活着。
3. 使用网络以确认网络连接没有断开。
保持连接(Keep Alive)处理中用到这个报文。
——PINGREQ报文没有可变报头。
——PINGREQ报文没有有效载荷。
响应:
服务端必须发送 PINGRESP报文响应客户端的PINGREQ报文。
服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。
保持连接(Keep Alive)处理中用到这个报文。
PINGRESP报文没有可变报头。
PINGRESP报文没有有效载荷。
DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
DISCONNECT报文没有可变报头。
DISCONNECT报文没有有效载荷。
响应:
客户端发送DISCONNECT报文之后:
—— 必须关闭网络连接 [MQTT-3.14.4-1]。
——不能通过那个网络连接再发送任何控制报文。
服务端在收到DISCONNECT报文时:
——必须丢弃任何与当前连接关联的未发布的遗嘱消息。
——应该关闭网络连接,如果客户端 还没有这么做。
为了提供服务质量保证,客户端和服务端有必要存储会话状态。在整个会话期间,客户端和服务端都必须存储会话状态 。会话必须至少持续和它的活跃网络连接同样长的时间。服务端的保留消息不是会话状态的组成部分。服务端应该保留那种消息直到客户端删除它。
MQTT协议要求基础传输层能够提供有序的、可靠的、双向传输(从客户端到服务端 和从服务端到客户端)的字节流。
无连接的网络传输协议如UDP是不支持的,因为他们可能会丢失数据包或对数据包重排序。
MQTT按照这里定义的服务质量 (QoS) 等级分发应用消息。分发协议是对称的,在下面的描述中,客户端和服务端既可以是发送者也可以是接收者。分发协议关注的是从单个发送者到单个接收者的应用消息。服务端分发应用消息给多个客户端时,每个客户端独立处理。分发给客户端的出站应用消息和入站应用消息的QoS等级可能是不同的。
qos0:最多分发一次
qos1:至少分发一次
qos2:仅分发一次
客户端设置清理会话(CleanSession)标志为0重连时,客户端和服务端必须使用原始的报文标识符重发任何未确认的PUBLISH报文(如果QoS>0)和PUBREL报文 [MQTT-4.4.0-1]。这是唯一要求客户端或服务端重发消息的情况。
服务端接管入站应用消息的所有权时,它必须将消息添加到订阅匹配的客户端的会话状态。正常情况下,客户端收到发送给它的订阅的消息。客户端也可能收到不是与它的订阅精确匹配的消息。如果服务端自动给客户端分配了一个订阅,可能发生这种情况。正在处理UBSUBSCRIBE请求时也可能收到消息。客户端必须按照可用的服务质量(QoS)规则确认它收到的任何PUBLISH报文,不管它选择是否处理报文包含的应用消息 。
实现本章定义的协议流程时,客户端必须遵循下列规则:
重发任何之前的PUBLISH报文时,必须按原始PUBLISH报文的发送顺序重发(适用于QoS 1和QoS 2消息)[MQTT-4.6.0-1]。
——必须按照对应的PUBLISH报文的顺序发送PUBACK报文(QoS 1消息)。
——必须按照对应的PUBLISH报文的顺序发送PUBREC报文(QoS 2消息。
——必须按照对应的PUBREC报文的顺序发送PUBREL报文(QoS 2消息)。
服务端必须默认认为每个主题都是有序的。它可以提供一个管理功能或其它机制,以允许将一个或多个主题当作是无序的 。
服务端处理发送给有序主题的消息时,必须按照上面的规则将消息分发给每个订阅者。此外,它必须按照从客户端收到的顺序发送PUBLISH报文给消费者(对相同的主题和QoS)。
斜杠(‘/’ U+002F)用于分割主题的每个层级,为主题名提供一个分层结构.
数字标志(‘#’ U+0023)是用于匹配主题中任意层级的通配符。
加号 (‘+’ U+002B) 是只能用于单个主题层级匹配的通配符。
服务端不能将 $ 字符开头的主题名匹配通配符 (#或+) 开头的主题过滤器.
$SYS/ 被广泛用作包含服务器特定信息或控制接口的主题的前缀。
应用不能使用 $ 字符开头的主题。
订阅 “#” 的客户端不会收到任何发布到以 “$” 开头主题的消息。
订阅 “+/monitor/Clients” 的客户端不会收到任何发布到 “$SYS/monitor/Clients” 的消息。
订阅 “$SYS/#” 的客户端会收到发布到以 “$SYS/” 开头主题的消息。
订阅 “$SYS/monitor/+” 的客户端会收到发布到 “$SYS/monitor/Clients” 主题的消息。
如果客户端想同时接受以 “$SYS/” 开头主题的消息和不以 $ 开头主题的消息,它需要同
时订阅 “#” 和 ““$SYS/#”。
除非另有说明,如果服务端或客户端遇到了协议违规的行为,它必须关闭传输这个协议违规控制报文的网络连接.
MQTT方案通常部署在不安全的通信环境中。在这种情况下,协议实现通常需要提供这些机制:
——用户和设备身份认证
——服务端资源访问授权
——MQTT控制报文和内嵌应用数据的完整性校验
——MQTT控制报文和内嵌应用数据的隐私控制
作为传输层协议,MQTT仅关注消息传输,提供合适的安全功能是实现者的责任。使用TLS[RFC5246] 是比较普遍的选择。
广泛采用高级加密标准 [AES] 数据加密标准 [DES] 。
推荐使用为受限的低端设备特别优化过的轻量级加密国际标准 ISO 29192 [ISO29192] 。
如果MQTT在WebSocket [RFC6455] 连接上传输,必须满足下面的条件:
——MQTT控制报文必须使用WebSocket二进制数据帧发送。如果收到任何其它类型的数据帧,接收者必须关闭网络连接 。
——单个WebSocket数据帧可以包含多个或者部分MQTT报文。接收者不能假设MQTT控制报文按WebSocket帧边界对齐 。
——客户端必须将字符串 mqtt 包含在它提供的WebSocket子协议列表里 。
——服务端选择和返回的WebSocket子协议名必须是 。
——用于连接客户端和服务器的WebSocket URI对MQTT协议没有任何影响。
MQTT规范定义了MQTT客户端实现和MQTT服务端实现的一致性要求
MQTT实现可以同时是MQTT客户端和MQTT服务端。接受入站连接和建立到其它服务端的出站连接的服务端必须同时符合MQTT客户端和MQTT服务端的要求 。
为了与任何其它的一致性实现交互操作,一致性实现不能要求使用在本规范之外定义的任何扩展 。
附录:
控制报文类型
byte1:标志位flags:
5. 【内部分享】MQTT协议解读及使用经验
时间:2018-07-26
Q: 什么是网络连接?
A: 网络连接是传输层定义的概念,在传输层以下只存在网络数据包的相互交换。
所谓连接,其实也不是在网络上有一条真实存在的数据通道。只要通信双方在一段时间内持续保持数据包交换,就可以视为双方建立的连接并没有断开。
连接的建立是依托于TCP协议的三次握手,一旦连接已经建立完毕,通信双方就可以复用这条虚拟通道进行数据交换。如果连接保持长时间工作一直没有被中断,那么这样的TCP连接就俗称为长连接。
Message Queue Telemetry Transport ,中文直译: 消息队列遥测传输协议 。
在MQTT协议被设计出来的年代,还没有物联网这么时髦的词汇,当年叫做 遥测设备 。
MQTT协议真正开始声名鹊起的原因,是其正好恰恰踩中移动互联网发展的节拍,为消息推送场景提供了一个既简便又具有良好扩展性的现成解决方案。
http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html
可以看出,MQTT对消息头的规定十分精简, 固定头部占用空间大小仅为1个字节 ,一个最小的报文占用的空间也 只有两个字节 (带一字节的长度标识位)。
这也是MQTT协议针对不稳定及带宽低下的网络环境做出的特定设计 - - - - 尽可能地节省一切不必要的网络开销 。
Q:为什么MQTT协议需要心跳报文(PINGREQ, PINGRESP)来维护连接状态,只监控该TCP的连接状态是否可以实现目的?
A: TCP数据传输默认的超时时间过长,不符合应用层上细粒度的要求。
TCP数据传输超时的情况可分成三种: 服务端断开 、 客户端断开 、 中间网络断开 。
在前两种场景下,若断开操作是一方主动发起的,即表示为TCP连接正常结束,双方走四次挥手流程;若程序异常结束,则会触发被动断开事件,通信另一方也能立刻感知到本次连接所打开的 Socket 出现中断异常。
唯独中间网络的状态是通信双方不能掌握的。 在Linux系统下 ,TCP的连接超时由内核参数来控制,如果通信中的一方没有得到及时回复,默认会主动再尝试 6次 。如果还没有得到及时回应,那么其才会认定本次数据超时。
连带首次发包与六次重试,Linux系统下这7次发包的超时时间分别为 2的0次方 至 2的6次方 ,即1秒、2秒、4秒、8秒、16秒、32秒、64秒,一共127秒。MQTT协议认为如此长的超时时间对应用层而言粒度太大,因此其在应用层上还单独设计属于自身的心跳响应控制。常见的MQTT连接超时多被设定为 60秒 。
扩展知识 - TCP的KeepAlive机制: http://hengyunabc.github.io/why-we-need-heartbeat/
由通信中的 报文标识符 ( Packet Identifier )传达。
Q:仅Publish与Pubrec能保证消息只被投递一次吗?
A: 业务上可以实现,但MQTT协议并没有如此设计,原因如下:
每个消息都会拥有属于自己的报文标识符,但如果需要两次数据交换就实现消息仅只收到一次,就需要通信双方记录下每次使用的报文标识符,并且在处理每一条消息时都需要去重处理,以防消息被重复消费。
但MQTT协议最初被设计的工作对象是轻量级物联设备,为此在协议的设计中报文标识符被约定为 可重用 ,以减少对设备性能的消耗,换回的代价不得不使用四次网络数据交换,才能确保消息正好被消费一次。
Q:两个不同客户端在发布与订阅同一Topic下的消息时,都可以提出通信Qos要求,此时以哪项为基准?
A: 伪命题,故意在分享时埋下坑,等人来踩。
两个不同客户端的通信是需要 Broker 进行中转,而不是直连。因此,通信中存在两个不同的会话,双方的Qos要求仅仅作用于它们与 Broker 之间的会话,最终的Qos基准只会向最低要求方看齐。
例:遗嘱消息的正确使用方式可参考此篇文章: https://www.hivemq.com/blog/mqtt-essentials-part-9-last-will-and-testament
虽然可以借助 Retain Message 实现绑定一条消息至某个Topic,以达到消息的暂时保留目的。
但首先 Retain Message 并不是为存储场景而设计的,再次MQTT协议并没有对消息的持久化作出规定,也就是说Broker重启后,现有保留消息也将丢失。
Q:两种特殊消息的使用场景?
A: 遗嘱消息,多用于客户端间获取互相之间异常断线的消息通知;
保留消息,可保存 最近一条 广播通知,多用于公告栏信息的发布。
Eclipse Mosquitto :MQTT协议的最小集实现
有 EMQ , HiveMQ , RabbitMQ MQTT Adapter 等。
Qos=2 消息保障的网络I/O次数过多,如果不是必需,尽少在程序里使用此类消息。
毕竟当初其设计的目的是为了减少设备的性能占用,但若应用场景并不是物联网,而是用于手机、电脑或浏览器端等现在已不缺性能的设备上,最好在报文体中,使用UUID生成全局唯一的消息ID,然后自行在业务解析中判断此报文是否被消费过。
或者,业务方在处理消息时保证其被消费的幂等性,也可消除重复消息对系统带来的影响。
正如MQTT协议并没有依赖TCP连接状态,自己在应用层协议上实现心跳报文来控制连接状态,业务方作为MQTT协议的使用者,也不要完全依赖协议的工作状态,而是依托MQTT协议建立属于业务本身的信息汇报机制,以加强系统的稳健性。
Retain Message 可视为客户端主动拉取的行为。如果业务系统采用 HTTP+MQTT 双协议描述业务过程,主动拉取的操作也可使用 HTTP 请求替代。
作为一个长连接型的应用,上线前需要根据业务量级,评估对操作系统 端口数 与 文件描述符 的占用要求,以防服务器资源被打满。
在服务端的配置文件和客户端的连接参数中,都拥有 max_inflight_messages 此项配置,来维护 Qos=1 or 2 消息是否被成功消费的状态。
MQTT 最初被设计为物联网级的通信协议,因此此参数的默认配额较小(大多数情况下被限制到10至20)。
但如果将MQTT协议应用至手机、PC或Web端的推送场景时,硬件性能已不在是瓶颈,在实际使用中推荐把此参数调大。
Mosquitto提供Bridge功能,需要我们自己配置。
Bridge 意为桥接,当我们把两台Broker桥接在一起时,只需要修改一台Broker的配置,填上另一台Broker的运行地址。前一台Broker将作为客户端发布与订阅后一台Broker的所有Topic,实现消息互通的目的。
桥接带来的问题有以下几点:
我的建议:
Websockets协议被设计的目的是为浏览器提供一个全双工的通信协议,方便实现消息推送功能。
在Websockets协议被设计出来前,受限于HTTP协议的一问一答模型,消息的推送只能靠轮询来实现,在资源消耗与时效性保障上,均难以达到令人满意的效果。
Websockets协议复用了HTTP协议的头部信息,告知浏览器接下来的操作将触发协议升级,然后通信双方继续复用HTTP的Header,但报文内容已转变为双方均接受的新协议的格式。
Websockets协议改进了网页浏览中的消息推送的方式,因此被广泛应用在聊天、支付通知等实时性要求比较高的场合下。
MQTT协议重点在于 消息队列的实现,其对消息投递的方式作出约定,并提供一些额外的通信保障 。
MQTT可采取原生的TCP实现,也有基于Websockets的实现版本。当然后者在网络字节的利用率上,不如前者那么精简。但浏览器端无法直接使用TCP协议,所以就只能基于Websockets协议开发。
不过基于Websockets的应用也有方便之处:一是证书不需要额外配置,直接与网站共用一套基础设施;二是可使用 Nginx 等工具管理流量,与普通HTTP流量可共用一套配置方法。
MQTT非常适合入门,原因如下:
实际的应用场景远比理想中的复杂,无法一招走遍天下,必须做好取舍。
MQTT协议在这方面做得很优秀,以后工作中可以作为参考,设计好自己负责的业务系统。
6. Mqtt介绍一
MQTT ( 消息队列遥测传输 )是ISO 标准(ISO/IEC PRF 20922)下戚坦基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族 上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个 消息中间件 。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的冲仔备环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
MQTT协议是为大量计算能力有限,且工作在低带宽、不可靠的网络的远程传感器和控制设备通讯而设计的协议,它具有以下主要的几项特性:
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份: 发布者(Publish) 、 代理(Broker) (服务器) 、订阅者(Subscribe) 。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为: 主题(Topic)和负载(payload) 两部分:
MQTT服务器以称为"消息代理"(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以:
在MQTT协议中,一个MQTT数据包由: 固定头(Fixed header)、可变头(Variable header)、消息体(payload) 三部分构成。MQTT数据包结构如下:
(1) 固定头(Fixed header) 。存在于所有散毁MQTT数据包中,表示数据包类型及数据包的分组类标识。
(2) 可变头(Variable header) 。存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
(3) 消息体(Payload) 。存在于部分MQTT数据包中,表示客户端收到的具体内容。
固定头存在于所有MQTT数据包中,其结构如下:
相于一个4位的无符号值,类型、取值及描述如下
在不使用标识位的消息类型中,标识位被作为保留位。如果收到无效的标志时,接收端必须关闭网络连接:
Payload消息体位MQTT数据包的第三部分,包含CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE四种类型的消息:
7. 一文读懂物联网的灵魂MQTT
名词释义:
MQTT——Message Queuing Telemetry Transport消息队列遥测传输
PUB——Publish发布
QoS——Quality of Service服务质量
LWT——Last Will & Testament最后遗嘱
MQTT简介
3.2 通配符
MQTT 中有两个可用的通配符,分别是+和#,+表示匹配单一层级中的任意主题,#表示匹配任意数量的层次。
3.3 服务质量(QoS)
MQTT 的设计初衷是为了在不可靠的网络中运作良好,为不同的场景提供了三个级别的歼贺服务质量,允许客户端指定自己想要的可靠性级别。
3.3.1 QoS Level 0:至多一次
这是最简单的级别,无需客户端确认,其可靠性与基础网络层 TCP/IP 一致。
3.3.2 QoS Level 1:至少冲岁一次,有可能重复
确保至少向客户端发送一次信息,不过也可发送多次;在接收数据包时,需要客户端返回确认消息(ACK 包)。这种方式常用于传递确保交付的信息,但开发人员必须确保其系统可以处理重复的数据包。
3.3.3 QoS Level 2:只有一次,确保消息只散改睁到达一次
这是最不常见的服务质量级别,确保消息发送且仅发送一次。这种方法需要交换4个数据包,同时也会降低消息代理的性能。由于相对比较复杂,在 MQTT 实现中通常会忽略这个级别,请确保在选择数据库或消息代理前检查这个问题。
3.4 “临终遗嘱”信息
MQTT提供了检测方式,利用KeepAlive机制在客户端异常断开时发现问题。因此当客户端电量耗尽、崩溃或者网络断开时,消息代理会采取相应措施。
客户端会向任意点的消息代理发送“临终遗嘱”(LWT)信息,当消息代理检测到客户端离线(连接并未关闭),就会发送保存在特定主题上的 LWT 信息,让其它客户端知道该节点已经意外离线。
3.5保留消息 Retained Messages
1个Topic只有唯一的retain消息,Broker会保存每个Topic的最后一条retain消息。
8. MQTT客户端设置
MQTTBox
从MQTTBox应用程序创建新的MQTT客户端时,您可以指定各种连接设置。大多数设置默认设置为最常用的值,但是您可能仍需要自定义设置以根据需要测试MQTT客户端。本文档详细解释了每个客户端设置属性,以便更好地理解客户端连接和实际的MQTT协议。
MQTT客户端名称: 用于标识MQTT客户端并在仪表板上显示的名称。它可以是任何字符串值。
例如:client_test_1
客户端ID: 客户端标识符是连接猛燃到MQTT代理的每个MQTT客户端的标识符。对于给定的经纪人,每个客户应该是唯一的。代理使用它来识别客户端和客户端的当前状态。它默认是自动生成的。如果尝试连接具有相同客户端标识符的两个MQTT客户端,则代理将拒绝连接。当您打开2个MQTTBox应用程序实例时,请确保您拥有唯一的客户端ID,否则您的客户将被代理拒绝并可能显示为脱机。
例如:client_id_1
将时间戳附加到MQTT客户端ID?: 如果选中,则时间戳将附加到客户端ID。默认情况下启用此选项。很多时候,用户在相同或不同的机器上打开多个MQTTBox应用程序实例,这些机器具有相同的MQTT客户端设置,包括不同的客户端ID。这会导致客户端连接由于相同的客户端ID而被代理拒绝。如果启用此选项,MQTTBox会将时间戳附加到每个客户端ID,使其几乎唯一,这有助于您节省调试不必要问题的时间。
Broker是否符合MQTT v3.1.1 ?: 如果要连接的MQTT代理仅支持旧版本的MQTT协议3.1或更低版本(v3.1.1是最新版本和当前标准版),则应取消选中此选项。默认情况下,它会被检查并假定MQTT代理符合当前的MQTT标准v3.1.1(或更高版本)。
协议: MQTT客户端用于连接MQTT代理的网络协议。
MQTTBox支持TCP,SSL / TLS,MQTT,MQTTS,WebSockets(WS)和Secure WebSockets(WSS)。根据您使用MQTTBox应用程序的平台,由于平台限制,可能不支持所有协议。
请 在此处 查看每个平台支持的MQTTBox功能列表。
主机: 要连接的MQTT主机。确保根据所选的MQTT连接协议指定正确的主机和端口号。如果您念丛提到错误的端口号或交换端口号,MQTT客户端可能无法连接。
例如:test.mosquitto.org:8080
Clean Session? :clean session标志指示代理是否客户端想要建立持久会话。如果您需要持久会话,即代理将存储客户端的所有订阅以及所有丢失的消息,则在订阅服务质量(QoS)1或2时,取消选中/不选择此选项。默认情况下会选中此选项或是,这意味着代理将启动新会话,不会为客户端存储任何内容,也会清除先前持久会话中的所有信息。
在应用程序启动时自动连接?: 如果您选中/是此选项,客户端将尝试在启动MQTTBox应用程序时自动连接到代理,并且可以处于“已连接”或“连接错误”状态,具体取决于客户端是否已连接到代理或不。如果未选中/没有此选项,客户端将处于“未连接”状态,您需要手动将客户端连接到代理。
您可以从MQTTBox仪表板连接,断开连接,重新连接MQTT客户端
用户名: 经纪人所需的用户名(如果有)。MQTT允许发送用于验证和授权客户端的用户名。
密码: 经纪人要求的密码(如果有)。MQTT允许发送密码以进行客户端的身份验证和授权。 请注意,所有密码都以纯文本格式保存。确保您永远不会在MQTTBox应用程仔知樱序中保存生产密码。事实上,所有字段都保存为纯文本,请确保您永远不会在MQTTBox应用程序中保存任何敏感信息/设置 。
重新安排Ping? 如果选中/是,则在发送数据包后重新安排ping消息。
队列传出QoS零消息: 如果选中/是,如果客户端和代理之间的连接断开,则客户端队列的传出QoS零消息。建立连接后,将发布所有这些消息。
重新连接周期(以毫秒为单位): 两次重新连接之间的间隔
连接超时(以毫秒为单位): 收到CONNACK之前等待的时间
KeepAlive(以秒为单位): keep alive是一个时间间隔,客户端通过向代理发送常规PING请求消息来提交。具有PING响应和这种机制的代理响应将允许双方确定另一方是否仍然存活且可达。默认设置为10秒,设置为0表示禁用。
Will Settings - will消息是MQTT Client最后遗嘱的一部分。当客户端断开连接时,它允许通知其他客户端。连接客户端将在CONNECT消息中以MQTT消息和主题的形式提供其意愿。如果此客户端无法正常断开连接,则代理会自动代表客户端发送此消息。
将设置 - 主题: 要发布的主题将是有效负载。一个简单的字符串,可以使用正斜杠作为分隔符进行分层结构化。
例如:topic_test_1或家庭/厨房/湿度
将设置 - QoS: 发布具有QoS集的有效负载。默认情况下为0。
将设置 - 保留: 保留will有效载荷的标志。
将设置 - 有效负载: 客户端断开连接时要发布的消息。
要发布 的主题 : 是要发布到的主题。一个简单的字符串,可以使用正斜杠作为分隔符进行分层结构化。
例如:topic_test_1或家庭/厨房/湿度
QoS: 此消息的服务质量等级(QoS)。级别(0,1或2)确定传递的消息的保证。
保留: 此标志确定代理是否将消息作为上一个已知的良好值保存到指定主题。订阅该主题的新客户将在订阅后立即收到有关该主题的最后保留消息。
要删除主题的保留消息,请发送包含该主题的零字节负载的保留消息。
有效负载: 这是要发布到主题的消息的实际内容。
主题: 是要订阅的String主题。您可以为主题指定单级(+)和多级(#)订阅。
例如:topic_test_1或家庭/ + /湿度或家庭/#
QoS: 此消息的服务质量等级(QoS)。级别(0,1或2)确定传递的消息的保证。
9. 阿里云微消息队列(MQTT)的基本使用
最近应系统功能需求,采购了一款云喇叭的物联网设备,就是插着4G卡那种,可以播放各种语音,仔细阅读了开发文档之改粗后发现使用的是MQTT的协议,记录一下在对接中遇到的各种凯核问题
MQTT是一个轻量的发布订阅模式消息传输协议,专门针对低带宽和不稳核孙镇定网络环境的物联网应用设计。
MQTT特点:
阿里云的MQTT有两个版本,这里只说没有RocketMQ依赖的3.1.1及以上版本。
这里会自动生成用户名密码
10. MQTT简单介绍
MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议),是一种基于发布/订阅模式的"轻量级"通讯协议,该协议构建于TCP/IP协议上。好比你给好友发送一封电子邮件,发送完成后你可以去做别的事情,收件人也不必立刻响应,可以在自己有空的时候查看邮件,是一个典型的异者弯步发布/订阅场景。而另一种典型的同步请求/回答场景,可以用接打电话的场景来类比。
MQTT的设计遵循以下的原则:
为了满足不同的场景,MQTT支持三种不同级别的服务质量(Quality of Service,QoS)为不同场景提供消息可靠性:
MQTT拥有14种不同的消息类型:
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:主题(Topic)和负载(payload)两部分:
MQTT会构建底层网络传输:它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的樱嫌蔽、基于字节流的双向传输。
当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关连。
一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以:
MQTT服务器以称为"消息代理"(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以:
订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。
每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。
连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。
一个对主题名通配符筛选器,在订阅表达式脊州中使用,表示订阅所匹配到的多个主题。
消息订阅者所具体接收的内容。
MQTT协议中定义了一些方法(也被称为动作),来于表示对确定资源所进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。主要方法有: