在本文中,我们将深入了解MQTT发布、订阅和取消订阅相关的内容。如果你刚接触发布/订阅模型,建议阅读本专栏之前的文章。
什么是MQTT发布消息
在MQTT中,一个客户端连接到代理(broker)之后可以立即发布消息。这些消息依据主题被过滤,每个消息必须包含一个主题,这样代理(broker)可用来将消息转发订阅的客户端。每条消息的有效载荷包括要以字节格式传输的数据,发送客户端可以选择发送任何类型的数据,包括文本、数字、图像、二进制数据,甚至是xml和json.
MQTT是数据无关的,意味着可根据客户端指定的应用场景构建有效负载数据。有效负载数据是消息的主要内容和客户端订阅、接收和处理的。
在一个MQTT发布的信息中有几个属性来确定消息的行为,包括包标识符、主题名称、服务质量(QoS)、保留标志、有效负载、DUP标志,接下来我们分别介绍。
MQTT包标识符(packetId)
在MQTT中,包标识符(packetId)是一个基本的属性。它用来标记指定的消息,确保这些消息按照它们发送的顺序转发出去,特别是当QoS等级大于0时。packetId是客户端分配的,包含在PUBLISH, PUBREL, PUBREC和 PUBCOMP消息中。当代理(broker)收到一条PUBLISH消息,客户端已经指定一个packetId给这条信息,然后会向客户端发送一条包含该packetId的PUBACK消息。客户端使用PUBACK消息确认代理(broker)收到了消息。在这个过程中,packedId保持不变。
在MQTT协议中,关于消息发送和消息确认,被分为几个阶段:
- 发布(PUBLISH): 这是该过程的第一阶段,涉及到客户端发送一条消息到代理(broker)。这条信息包含一个主题和有效负载。
- 发布已接收(PUBREC): 收到PUBLISH消息后,代理(broker)会发送一条PUBREC消息来确认其已经收到消息。这是该过程的第二阶段。
- 发布释放(PUBREL): 一旦客户端收到PUBREC消息,它就会发送一条PUBREL消息,以解除代理(broker)将消息保存在内存中的责任。这是该过程的第三阶段。
- 发布完成(PUBCOMP): 代理(broker)最后发送一个PUBCOMP消息来确认它已经收到并处理了消息。这是该过程的第四阶段。
这四条消息是MQTT协议QoS的部分机制,确保消息的稳定发送。QoS等级决定了客户端和代理(broker)之间交换消息的数量。
值得注意的是,数据包标识符在客户端和代理(broker)之间流动时唯一标识消息。数据包标识符仅与大于0的QoS级别相关。不不仅适用于PUBLISH,也适用于SUBSCRIBE、UNSUBSCRIBE和CONNECT消息。
客户端库负责设置此内部MQTT标识。当QoS等级大于0时,客户端必须在发送下一条消息之前,必须等待一条来自代理(broker)的PUBACK或PUBREC消息。客户端同样需要跟踪它发送和接收的数据包标识符,确认消息没有丢失或重复。总之,数据包标识符对于MQTT的稳定机制非常重要,确保正确、高效的发送消息。
MQTT主题名称(topicName)
MQTT使用主题名称作为基本概念。它使用正斜杠作为分割符分层构造此名称,并创建一个简单的字符串。类似一个URL,但没有协议类型和域名。MQTT主题用来标记信息,为客户端提供一个订阅指定消息的途径。
例如,一个测量湿度的设备可能会发布它的值到主题“sensors/temperature/livingroom”。对这些值感兴趣的客户端可以订阅这个主题,接收更新后发布的值。
MQTT提供两类通配符,用在主题订阅上:
- “+”(加号) 用来匹配单个层级。比如,评阅“sensors/+/livingroom”,会匹配“sensors/temperature/livingroom”和“sensors/humidity/livingroom”,但不会匹配“sensors/temperauture/kitchen”。
- “#”(井号) 用来匹配多个层级。比如,订阅“sensors/#”,会匹配“sensors/temperature/livingroom”、“sensors/humidity/kitchen”和"sensors/power/meter1".
订阅大量主题会对代理(broker)的性能有重大影响。这是因为发布到客户端订阅的主题的每条消息都必须传递到该客户端。如果很多客户端订阅了很多主题,这会迅速变成代理(broker)的沉重负担。
使用通配符,通过一个订阅来订阅多个主题同样影响性能。当一个客户端订阅一个含有通配符的主题,代理必须评估发送来的每条消息来匹配主题,以确定是否将消息转发给该客户端。如果匹配的主题很多,这就会限制代理(broker)的资源。
为了避免性能问题,高效的使用主题订阅非常重要。一个重要原则就是尽可能使用确定的主题过滤,而不是依赖通配符。另一个原则就是使用分享订阅,,就是允许多个客户端分享一个主题的订阅。这就会减少订阅量和代理(broker)必须处理的消息。最后,监控代理(broker)的性能,根据需要调整配置来确保最优性能也很重要。
MQTT中的QoS
在之前文章中(http://t.csdnimg.cn/zJblm)简单提过QoS。QoS由一个介于0到2之间的数字表示。每个层级都为消息传递提供不同级别的可靠性和保证。
- QoS 0(最多一次): 这个等级并不保证消息被发送出去。消息被发送一次,如果丢失或接收端没有收到,消息不会重新发送。
- QoS 1(至少一次): 这个等级消息至少被发送一次,但是,可能会因为网络原因或发送失败,发送多次。
- QoS 2(确切一次): 这个等级为消息发送提供最高等级的保证。确保消息发送一次,但是这个等级会要求发送方和接收方多次通信,会增加网络延迟和网络流量。
根据具体的应用场景,选择合适的QoS等级。比如,QoS 0可能适用于是重要的数据,QoS 2可能对重要的、需要高稳定等级的数据很有必要。
需要注意的是,QoS等级会影响代理(broker)和网络的性能,所以建议根据具体的应用场景选择合适的等级。关于这部分,后续会深入讨论。
MQTT中的保留标志(retainFlag)
保留标志是一项重要功能,用于确定代理(broker)是否将消息保存为指定主题的最后一个已知良好值。当保留标志被设置成true时,代理(broker)将会保留匹配指定主题的最近的消息,无论是否有订阅的客户端。
当有一个新的客户端订阅有保留消息的主题时,代理(broker)就会发送最后保留的消息给该客户端。这就允许客户端接收最近的相关信息,即使之前没有订阅该主题。
同样需要注意的是,类似前面提到的其他元素,保留标记的使用也会影响代理(broker)的性能,特别是当有很多保留消息时。另外,如果一个保留消息经常被更新,就会增加网络流量,可能影响网络的性能。
更多关于保留标志的内容及如何使用保留标志,会在后续文章中介绍。
MQTT中的负载(payload)
负载是消息中的实际内容,可以包含任意类型的数据。MQTT是数据无关的,意味着可以处理不同的数据类型,包括图像、任意编码的文本、加密数据和二进制数据。但是,要注意的是,负载的大小会影响网络的性能、客户端和代理的内存使用。因此,建议使负载尽可能的小,特别是当高频率的推送数据时。
MQTT中的DUP标志(dupFlag)
DUP标志表明这个消息是一个复本,因为原接收端(客户端或代理)没有确认最初的消息,所以被重新发送。只和QoS等级大于0的消息相关。当一个客户端或代理(broker)收到一个有DUP标记的消息,如果和已经收到的消息的ID相同,则应该忽略该消息。如果之前没有收到,则应该正常处理该消息。
MQTT协议(MQTT客户端库或代理)自动处理重发和复制机制,但需要注意的是这会影响网络性能,增加网络流量。更多内容,会在后续文章中介绍。
MQTT代理(broker)如何处理来自客户端的消息
当一个客户端发布消息到MQTT代理(broker),代理(broker)会进行几项工作来确保根据客户端指定的QoS等级将消息发送出去。具体如下:
- 消息接收: 代理(broker)读取客户端发送的消息,验证消息的语法和格式。
- 确认: 代理(broker)发送一个确认消息到客户端,来确认收到了消息。确认的等级依赖于QoS等级。
- 处理: 代理确认哪些客户端订阅了该消息的主题,将消息的复本发送给它们当中的每一个。依据保留标志,代理可能也会保留该主题的最后的好的消息。
- 反馈: 发布消息的客户端收到来自代理的确认消息,指示该消息已成功发送。但是,客户端不会收到有多少订阅者收到消息的反馈或是否有客户端对该消息感兴趣。
最初发送消息的客户端只关心发送到代理的PUBLISH消息。代理(broker)一旦收到PUBLISH消息,其责任就是将消息发送给所有的订阅者。发送的客户端不会收到任务关于是否有人对发送的消息感兴趣或有多少客户端从代理(broker)收到了该消息的反馈。
如何订阅MQTT主题
如果没有人接收,发布消息将没有任何意义,这就是订阅的意义。一旦一个客户端发布一条消息到MQTT代理(broker),该消息必须被发送给对其感兴趣的客户端。对该主题有兴趣的、希望接收该信息的客户端发送一个SUBSCRIBE消息到代理(broker)。SUBSCRIBE消息很简单,包含一个唯一的标识符ID(packetId)和一个订阅列表。
标识符ID(packetId): 标识符ID是唯一的,标记一条在客户端和代理(broker)之间流动的消息,客户端负载设置此内部MQTT标识符。
订阅列表: 一条SUBSCRIBE消息能够包含多条订阅。每个订阅包括一个主题和一个QoS等级。主题可以包含通配符,使订阅主题模式而不是一个确定的主题成为可能。如果有重叠的订阅,代理(broker)会发送该主题高QoS等级的消息给客户端。
总之,MQTT允许客户端订阅指定的主题,接收发布到这些主题的消息,根据它们的应用场景处理负载数据。SUBSCRIBE消息中的标识符ID(packetId)和QoS等级确保消息以合适的4质量等级被可靠的发送。
一个客户端一旦发送一条包含期望主题和QoS等级的SUBSCRIBE消息到MQTT代理(broker),代理(broker)用一条SUBACK消息回复,确认订阅,指定代理会发送的最高QoS等级。
MQTT的Suback
客户端一旦发送一条包含主题和QoS等级的消息到代理(broker),代理(broker)通过发送一条SUBACK消息到客户端来确认订阅请求。SUBACK消息确认收到了SUBSCRIBE消息,表明代理(broker)已经接受或拒绝每个订阅。
包标识符(packedId): SUBACK消息中包含着和SUBSCRIBE消息中同样的包标识符,确保客户端和开始的请求匹配确认。
返回码(returnCode): SUBACK消息中还包括对应SUBSCRIBE消息中每一对主题/QoS的返回码。返回码的值是二进制,指示代理(broker)是已批准还是拒绝每个主题的订阅请求。
QoS等级的返回码如下:
- QoS 0: 意味着订阅请求被授予QoS 0 等级。消息一旦可用,代理(broker)马上发送消息到客户端,不保证质量。
- QoS 1: 意味着订阅请求被授予QoS 1 等级。代理(broker)至少发送一次消息,意味着代理(broker)至少发送一次消息到客户端。客户端收到消息后返回一条PUBACK消息进行确认。
- QoS 2: 意味着订阅请求被授予QoS 2 等级。代理(broker)确切的发送一次消息,意味着代理(broker)保证消息被发送一次且只有一次到客户端。客户端收到消息后返回一条PUBREC消息到代理(broker)进行确认。代理收到PUBREC消息后发送一条PUBREL消息到客户端,然后客户端收到PUBREL消息后发送一条PUBCOMP消息到代理(broker)。
如果代理拒绝 SUBSCRIBE 消息中的任何订阅,那么 SUBACK 消息将包含该特定主题的失败返回码。失败的原因可能是客户端没有足够的权限来订阅主题、主题格式不正确或其他原因。
失败返回码由0x80表示,指示代理不接受订阅。如果客户端没有足够的权限来订阅主F题、主题格式不正确或订阅请求存在其他问题,则可能会发生这种情况。当客户端收到一个失败返回码,它需要使用不同的主题或QoS等级来重新订阅或采取合适的措施解决订阅请求的问题。
Return Code | Return Code Response |
---|---|
0 | Success - Maximum QoS 0 |
1 | Success - Maximum QoS 1 |
2 | Success - Maximum QoS 2 |
128 | Failure |
SUBACK是来自代理(broker)、发送到客户端的确认请求被接受或拒绝的消息。数据包标识符(packedId)使客户端能够将确认与原始请求相匹配,而返回代码指示代理授予订阅的 QoS 级别。
当客户端评阅了它感兴趣的主题,并且收到发布到这些主题的消息,它最终可能需要取消订阅。接下来让我们看下SUBSCRIBE消息相对的部分:UNSUBSCRIBE消息和确认取消订阅的UNSUBACK消息。
如何使用MQTT中的退订功能撤销订阅?
在 MQTT 中,客户端可以通过向代理发送 UNSUBSCRIBE 消息来取消订阅它们已订阅的主题。与 SUBSCRIBE 类似,此消息包括用于唯一标识它的数据包标识符和要取消订阅的主题列表。
包标识符(packetId): 和SUBSCRIBE消息中的类似,UNSUBSCRIBE 消息中的数据包标识符用作客户端和代理之间消息流的内部 MQTT 标识符。它确保客户端和代理可以跟踪消息及其相应的确认消息。
主题列表: 取消订阅消息中的主题列表可以包含客户端要取消订阅的一个或多个主题。无需指定 QoS 级别,因为无论最初订阅的 QoS 级别如何,代理都会取消订阅主题。
MQTT Unsuback
当收到UNSUBSCRIBE消息后,代理(broker)发送一条UNSUBACK消息来确认客户端订阅的删除。这个消息包括UNSUBSCRIBE消息的包标识符(packetId),作为代理(broker)已经成功从客户端的订阅列表中移除主题的确认。
数据包标识符: UNSUBACK消息中的数据包标识符与对应的UNSUBSCRIBE消息中的数据包标识符相同。这可确保客户端可以识别确认消息并将其与原始取消订阅消息相关联。
返回码: UNSUBACK 消息包含已取消订阅的每个主题/QoS 对的返回代码列表。返回代码 0 表示删除成功,而返回代码 17 表示由于主题无效或格式不正确而删除不成功。还可以为不同的错误方案指定其他返回代码。
从代理(broker)收到 UNSUBACK 后,客户端可以假定 UNSUBSCRIBE 消息中的订阅已被删除。
通过这些详细信息,可以全面了解客户端如何取消订阅主题,以及代理如何分别通过 UNSUBSCRIBE 和 UNSUBACK 消息确认删除这些订阅。
结论
MQTT 提供了一种灵活且与数据无关的方法,用于在客户端和代理(broker)之间发布消息。通过使用主题过滤消息,客户端可以快速轻松地订阅它们感兴趣的内容。可以自定义每条消息的有效负载(payload)以满足每个客户端的特定需求,MQTT 对各种数据类型的支持使其成为许多用例的多功能解决方案。此外,了解 PUBLISH 消息的属性(如 QoS 级别和保留标志)可以帮助客户端和代理确保高效可靠地传递消息。
后续的文章中,会详细讨论主题(topic)相关的内容,如何使用通配符等。