作者: HiveMQ Team
发布于: 2018年1月8日
创建于: 2023年6月29日
协议的基础变化
欢迎来到MQTT 5基础系列课程的第二部分,在第一部分中我们介绍了MQTT 5协议,我们探究了MQTT的基本原理,讨论它的起源和它在IoT领域的重要性,并对它的基本概念和特征做了一个概述。在第二部分,我们将深入介绍MQTT 5的变化,了解这些变化是如何改善IoT协议的效率,并密切讨论 MQTT 5与MQTT 3。
MQTT 5在MQTT 3.1.1之上有了显著的提升,与其说是一次革命不如说是演变,因为MQTT 5坚定的保留了所有让其成功的特性。它的轻量级,推送通信,独特属性,易用性,异常的可伸缩性非常适合移动网络 ,并且也保留了所有参与通信的解耦特性,但是也增加了一些基础特性机制,同时也对做了稍微的调整,主要为了确保使用新版本依然有MQTT的感觉。由于MQTT 5的改善部分强化了标准,因此让它成为至今为止最受欢迎的IoT协议。
本篇文章,我们将分析MQTT 5规范中你需要了解所有基础变化。而且也会给即将发表的关于深究新特性详情的文章做好铺垫。
对比MQTT 3,MQTT 5改善了什么?
对于熟悉MQTT 3.1.1的人来说,好消息是:MQTT 3.1.1 中的基础规则和特性在MQTT 5中仍然保留。如果你是一个MQTT新手,为了你能有一些基础理解,我们建议你先学习MQTT Essentials series。尽管MQTT 5进行了一些修改和扩展,但你所熟悉的MQTT核心依然在。值得注意的是,调整部分包含先前已有特征的变化,比如:Last Will and Testament, 也包含流行的HiveMQ部分特性(如:TTL and Shared Subscriptions)。
底层协议发生了一些微小的变化,而且新增了一个AUTH控制包。但是这些变化并没有让MQTT的核心标志变得模糊不清。MQTT 5依然是原来的MQTT,即使它的能力已增强,但还是保留了它可识别的特征。
在MQTT 5的数据头和Reason Codes中,有哪些属性?
毫无疑问,MQTT 5中引入的最具变革性和灵活性的特性之一是在MQTT报头中可录入自定义键值属性。这种能力可能会给许多部署带来革命性的变化,关于它的重要性,需要一篇完整的博客文章来专门介绍它。与HTTP 之类的协议一样,MQTT客户端和代理服务器可以添加无限数量的自定义或预定义的头来携带元数据。这种元数据是可适应的,可以根据特定的应用程序数据进行定制,预定义的头主要用于执行大多数新的 MQTT 特性。
另外,MQTT数据包现在包含了Reason Codes,表示发生了预定义的协议错误。这些Reason Codes通常可以在确认数据包(即ACK数据包)中找到,它们有助于解释错误,并可能有助于客户端和代理设计补救措施。
Reason Codes偶尔会被叫做负反馈(非译内容:全称:Negative Acknowledgements,缩写:NACK, 如需了解,参见文章末尾解释),会出现在一些MQTT数据包中,如下:
- CONNACK
- PUBACK
- PUBREC
- PUBREL
- PUBCOMP
- SUBACK
- UNSUBACK
- AUTH
- DISCONNECT
关于NACK机制中的Reason Code值范围非常大,从“超额”到“协议错误”(非译内容:关于Reason Code, 具体参见规范“2.4 Reason Code”)。解码和理解这些新引入Reason Code是客户端和代理服务器共同的责任,这将进一步丰富整个MQTT体验。
你已经了解了自定义键值属性和Reason Code,现在让我们探索MQTT如何处理不受支持的特性并使用 CONNACK 返回代码。
用 CONNACK 返回码响应不支持的特性
随着MQTT的普及,由各种各样的公司实现的MQTT已出现。然而,并非所有公司严格遵守MQTT规范去实现协议。某些特性(如 QoS2、保留消息或持久会话)可能无法逐一地实现。相比之下,HiveMQ仍然完全符合 MQTT规范,全面支持所有特性。围绕不受支持的特性这一主题,MQTT 5提出了一个巧妙的解决方案。为了表示代理服务器不支持某些特性,它支持“哪些特性的能力不被支持”(通常在 SaaS 产品中看到),然后将处理权交给客户端,由客户端确保这些特性不再被使用。代理服务器在CONNACK数据包中使用预定义的报头,以响应客户端CONNECT数据包,从而传递对特定功能缺乏支持的信息。还可以利用这些头来通知客户机它们不允许使用某些特性。
MQTT 5中,可用预定义头有哪些?
以下是 MQTT v5中可用的预定义头部,用于指示未实现的特性或未授权给客户端使用的特性:
| 预定义头 | 数据类型 | 描述 |
|---|---|---|
| Retain Available | Boolean | 代理服务器是否支持预留消息 |
| Maximum QoS | Number | 客户端允许用于发布消息或订阅主题的最大 QoS |
| Wildcard available | Boolean | 适配是否符可用于订阅 |
| Subscription identifiers available | Boolean | 订阅标识是否有效 |
| Shared Subscriptions available | Boolean | 共享订阅对客户端是否有效 |
| Maximum Message Size | Number | 定义 MQTT 客户端可以使用的最大消息尺寸 |
| Server Keep Alive | Number | The Keep Alive Interval 服务端为客户端提供的保活时间间隙 |
这些返回代码代表了MQTT客户端在权限方面向前迈出了重要的一步。然而,这种新功能带有一种内在的权衡; MQTT客户端必须独立地实现对这些代码的解释,并且必须确保应用程序开发人员避免使用代理服务器不支持的特性,或者那些客户端没有权限的特性。
值得注意的是,HiveMQ完全兼容所有MQTT 5特性,并确保这些自定义头只能由管理员权限的账户在部署中设置权限。
增加会话管理: 从 Clean Session 到 Clean Start
MQTT 3.1.1中的一个突出特性“Clean Session”的概念现在由MQTT v5中的“Clean Start”取代。由于客户端的临时连接或者没有订阅消息,“clean Session”被高频率在MQTT 3.1.1中使用。连接到代理服务器之后,客户端需要发送一个启用或禁用Clean Session标志的CONNECT数据包。如果启用,代理服务器会丢弃所有基于TCP连接的数据。ou TCP 连接被切断时,或者当客户端决定从代理断开连接时,应该丢弃所有客户端数据。此外,如果前一个会话与代理服务器上的客户端标识符相关联,则Clean Session CONNECT数据包将迫使代理放弃前一个数据。此外,如果前一个会话与代理服务器上的客户端标识符相关联,则 Clean Session CONNECT 数据包将迫使代理放弃前一个数据。
MQTT 5引入了Clean Start选项,在CONNECT消息中通过Clean Start标志发出信号。使用这个标志,代理服务器将删除任何以前的会话数据,客户端将启动一个新的会话。但是,当客户端和代理服务器之间的TCP连接关闭时,会话不会自动清除。若要在断开连接后提示删除会话,必须将新的标头字段 Session ExiryInterval设置为0。
修订后的Clean Start功能增强和简化了MQTT的会话处理,与以前的Clean Session/持久会话概念相比,提供了更多的灵活性和更容易的实现。在MQTT 5中,除非“Session Expiry Interval”设置为0,否则所有会话都将持续。会话删除发生在间隔超时之后或客户端使用Clean Start重新连接时。
MQTT 5中,什么是AUTH数据包?
在一个令人兴奋的开发中,MQTT5提出了一种新的数据包类型: AUTH 数据包。作为十分重要的验证机制工具,我们期望这个数据包在生产环境中是不可或缺的。我们将在另一篇文章中深入研究它的确切语义。
了解由代理服务器和客户端连接后发送的这个新颖的数据包这件事是至关重要的。它支持使用复杂的挑战/响应身份验证方法,如SASL框架中概述的SCRAM 或 Kerberos,并且还与OAuth等尖端IoT身份验证方法兼容。重要的是,AUTH数据包允许对MQTT客户端进行重新身份验证,而不需要终止连接。
MQTT 5: 用“UTF-8字符串对”增强了MQTT
为了适应自定义头,需要一个新的数据类型,因此引入了“UTF-8字符串对”。本质上,字符串对是“键-值”结构,其中键和值都是String数据类型。目前,这种数据类型主要用于自定义头。
这种新颖的做法扩大了MQTT的数据类型,总共增加了7种:
- Bit
- Two Byte Integer
- Four Byte Integer
- UTF-8 Encoded String
- Variable Byte Integer
- Binary Data
- UTF-8 String Pair
对于大多数应用程序用户来说, Binary Data和 UTF-8 Encoded仍然是MQTT库API中的常用数据类型。然而,随着MQTT 5的出现,预计“UTF-8字符串对”将得到频繁的使用。
虽然其余的数据类型可能不能直接对用户可见,但是可以通过MQTT 客户端库和代理服务器在线上利用它们来构造有效的MQTT数据包。
MQTT 5如何增强双向DISCONNECT数据包的通信?
在MQTT 3.1.1框架下,客户端可以在关闭底层TCP连接之前通过分派DISCONNECT数据包优雅地终止连接。但是,没有规定MQTT代理服务器需要关闭TCP连接有潜在问题的客户端。这个缺点已经在新的协议版本中得到了解决。
在改善过的MQTT中,代理服务器被授权在切断套接字连接之前传输DISCONNECT数据包。这项规定使用户能够了解断开连接的原因,并据此制定适当的应对措施。虽然代理服务器没有被要求披露确切的原因(例如,出于安全目的),但是这个特性有利于开发人员,因为它提供了关于代理服务器为什么终止连接的有价值的原因。
另一个价值是DISCONNECT数据包现在可以携带Reason Code,简化了说明断开连接的原因的过程,例如在权限无效的情况下。
改进MQTT 5中的 QoS 1和2: 为了健康连接,消除重试
MQTT为底层传输使用持久的TCP连接(或具有相同保证的类似协议)。一个健壮的TCP连接可以确保双向连接的一次性和有序性,这意味着客户端或代理发送的所有 MQTT 数据包都将在另一端接收。当消息在传输过程中TCP连接被中断时,QoS 1和2确保跨多个TCP连接传递消息。
在 MQTT 3.1.1协议下,即使TCP连接是健康的,也允许重新传递MQTT消息。这在实践中经常被证明是有害的,可能会使已经负担沉重的MQTT客户端超载。考虑这样一个场景: MQTT客户端需要11秒处理从代理服务器接收的消息,以便在处理后确认数据包。如果代理服务器在10秒钟的超时后重新传输消息,则没有明显的优势。这种方法只是消耗了宝贵的带宽,并进一步使 MQTT 客户机不堪重负。
随着 MQTT 5的出现,代理服务器和客户端都不允许为健康的TCP连接重新传输MQTT消息。但是,当 TCP 连接被切断时,代理服务器和客户端必须重新发送未确认的数据包。因此,QoS 1和2仍然和MQTT 3.1.1中一样重要
随着 MQTT 5的出现,代理和客户端都不允许为健康的 TCP 连接重新传输 MQTT 消息。但是,当 TCP 连接被切断时,代理和客户端必须重新发送未确认的数据包。因此,QoS1和2保证仍然和 MQTT 3.1.1中一样重要。
MQTT 5如何简化身份验证?
在 MQTT 3.1.1协议中,当使用CONNECT数据包中的密码时,MQTT客户端需要提供用户名。对于一些不需要用户名的场景来说,是不方便的。有一个突出的例子,使用 OAuth ,它利用一个JSON web令牌来获取身份验证和授权信息。对于MQTT 3.1.1通常使用静态用户名,因为关键信息包含在密码字段中。
随着MQTT 5的出现,协议引入了更精确的方法来处理令牌,例如,通过AUTH数据包。但是,使用 CONNECT数据包的密码字段仍然是可行的。这里的主要改进是,用户可以简单地利用密码字段,而不必填写用户名。此调整为特定场景中的身份验证提供了一种更加简化和直接的方法。
总结: 揭开 MQTT 5的基础
虽然MQTT协议的核心仍然相对不变,但是已经在表面下实现了微妙的改进,为这个被广泛采用的IoT协议的第5版中的许多新特性奠定了基础。作为一个使用MQTT库的用户,这些修改可能看起来很小,并不会从根本上改变MQTT的使用方式。然而,对于使用MQTT库和代理服务器的开发人员来说,这些更改,特别是那些与协议细微差别相关的更改,是至关重要的,需要关注。