[译] MQTT 主题和最佳实践

image

topic 的数量随着业务的增长逐渐增多,如何正确的设计 topic 成了当务之急。在这篇文章中,我们将重点介绍 MQTT 主题和最佳实践。 正如我们已经提到的,主题用于决定 MQTT 代理哪个客户端收到哪个消息。 后面我们还将讨论 SYS-topics,这是关于代理内部信息的特殊主题。那么我们开始吧。

主题 (Topics)

topic 是 UTF-8 字符串,代理用于过滤客户端的消息。一个主题由一个或多个主题层组成。每个主题级别由正斜杠(主题级别分隔符)分隔。
image
与消息队列相比,话题非常轻便。在发布或订阅它之前,客户端不需要创建所需的主题,因为代理接受每个有效的主题,而无需事先进行初始化。

以下是一些主题示例:

myhome/groundfloor/livingroom/temperature
USA/California/San Francisco/Silicon Valley
5ff4a2ce-e485-40f4-826c-b1a5d81be9b6/status
Germany/Bavaria/car/2382340923453/latitude

注意:每个主题必须至少有一个有效的字符,可以包含空格。主题是区分大小写的, 所以 myhome/temperatureMyhome/Temperature 是不同的主题。另外,斜线本身也是一个有效的话题。

通配符(Wildcards)

当客户端订阅一个主题时,客户端可以订阅一个确切主题或者使用通配符一次订阅更多的主题。通配符只能在订阅主题时使用,在发布消息时不允许使用。下面我们将逐一介绍两种不同的类型:单层和多层通配符。

单级通配符: ‘+’

正如名字所暗示的那样,单一级别的通配符可以代替一个主题级别。加号表示主题中的单个级别通配符。
image
如果包含任意字符串而不是通配符,则任何主题都与包含单级通配符的主题相匹配。例如,对myhome / groundfloor / + / temperature的订阅将与以下主题匹配或不匹配:
image

多级通配符: ‘#’

单级通配符只覆盖一个主题级别,而多级别通配符则覆盖任意数量的主题级别。为了确定匹配的主题,多级通配符需必须是主题中的最后一个字符,并且前面有一个正斜杠。
image
image
客户端订阅一个具有多级通配符的主题时,将接收所有消息,这些消息以通配符前的模式开始,无论主题将会有多长或多深。如果您只是将多级通配符指定为一个主题(#),那么就意味着您将会得到发送到 MQTT 代理上的所有消息。如果您期望高吞吐量,这不是一个最佳模式,请参见下面的最佳实践。

以 ‘$’ 开头的主题

一般而言,你可以随意命名你的主题,但是有一个例外。以 ‘$-‘ 符号开头的主题将被特别处理,例如:在订阅 # 时,这不属于客户端订阅的一部分。因为这些主题是为 MQTT 代理的内部统计数据保留的。因此,客户端不可能向这些主题发布消息。目前还没有明确的官方标准化的主题,必须由代理发布主题。使用 $SYS/ 获取所有这些信息是很常见的做法,大部分代理都实现了,只是格式不同。关于$SYS-topics 的一个建议是在 MQTT GitHub wiki 中。

这里有一些例子:

$SYS/broker/clients/connected
$SYS/broker/clients/disconnected
$SYS/broker/clients/total
$SYS/broker/messages/sent
$SYS/broker/uptime

总结

这些是关于 MQTT 消息主题的基础知识。正如您所看到的,MQTT主题是动态的,并且给它的创建者提供了很大的灵活性。但是在实际应用中使用这些工具时,您应该注意到一些挑战。我们收集了我们的最佳实践,在过去的一年里,我们在各种项目中过度使用了MQTT。在评论中,我们对其他建议或讨论持开放态度,因此,让我们知道你的最佳做法,或者如果你不同意我们的最佳做法之一!

最佳实践

不要以 ‘/‘ 开头

在 MQTT 里允许使用 ‘/‘ 开头的主题,例如 /myhome/groundfloor/livingroom。但这引入了一个不必要的零字符的主题级别。应该避免这么做,因为它没有任何好处,反而会导致主题混乱。

在主题里不要使用空格

空格是程序员的天敌,当程序出现异常时,含有空格的主题不容易阅读跟调试。与第一条观点类似,空格应该避免被使用。

保持主题简明

每条信息中都会包含主题,因此你应该考虑保持主题简单明了。特别是在小型设备上,每个字节数都十分重要。

避免使用不可打印的字符,只用 ASCII 字符集

使用非 ASCII 的 UTF-8 字符集可能很难找出因为错别字或者字符引起的问题, 因为这些字符不能被正确地显示。除非必须使用,否则应该避免在主题中使用非 ASCII 字符。

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

在一些场景下这种做法是非常有效的,可以帮助识别发送消息者。另外一个优点是身份验证,以便于让客户端只被允许发布与自身 ID 相关的主题。所以,一个带有 client1 的客户端只被允许发布 client1/status 主题, 但无法发布 client2/status 主题。

不要订阅

某些情况下,我们需要订阅经过代理服务器传输的所有的消息,例如将消息持久化到数据库中的操作。但是我们不应该使用订阅多级通配符的方式去实现该功能。原因是订阅的客户端通常无法处理即将到来的消息负载,特别是如果你的吞吐量很大的时候。我们推荐的解决方法是在 MQTT 代理服务器上实现扩展功能,例如加入 HiveMQ 系统插件, 它允许你加入 HiveMQ 的钩子并且添加一个异步的进程去处理每一条消息,并将之保存到数据库中。

不要忘记可扩展性

主题是一个灵活的概念,不需要预先为其分配空间,但发布者和订阅者双方都应该知晓主题。所以思考如何能在添加新功能时仍可以很好地扩展当前主题就显得尤为重要。例如,当你的智能家居系统需要增加一些新的传感器时,应该可以在不改变主题架构的前提下将其添加进去。

使用具体的主题,而不是通用的主题

在命名主题时,重要的是不要像队列那样使用命名,例如所有的消息都使用同一个主题是错误的。你应该尽可能的使主题具体化。如果你有三个传感器在卧室中,你应该这样命名主题:myhome/livingroom/temperature,myhome/livingroom/brightness 以及 myhome/livingroom/humidity,而不是通过myhome/livingroom发送所有的值。这样也便于你使用其他的 MQTT 功能,例如留存信息,这些我们会在下一章提到。

原文:https://www.hivemq.com/blog/mqtt-essentials-part-5-mqtt-topics-best-practices