MQTT Topics & Best Practices - MQTT Essentials: Part 5
Written by The HiveMQ Team
Category: MQTT Essentials
Published: August 20, 2019
Welcome to the fifth part of MQTT Essentials. A ten-part blog series on the core features and concepts of the MQTT protocol. In this post, we focus on MQTT topics and best practices. As we have already mentioned, the MQTT broker uses the topic of a message to decide which client receives which message. We also look at SYS-topics, which are special topics that reveal information about the broker itself. Let’s get started.
In MQTT, the word topic refers to an UTF-8 string that the broker uses to filter messages for each connected client. The topic consists of one or more topic levels. Each topic level is separated by a forward slash (topic level separator).
In comparison to a message queue, MQTT topics are very lightweight. The client does not need to create the desired topic before they publish or subscribe to it. The broker accepts each valid topic without any prior initialization.
Here are some examples of topics:
USA/California/San Francisco/Silicon Valley
Note that each topic must contain at least 1 character and that the topic string permits empty spaces. Topics are case-sensitive. For example, _myhome/temperature and _MyHome/Temperature are two different topics. Additionally, the forward slash alone is a valid topic.
When a client subscribes to a topic, it can subscribe to the exact topic of a published message or it can use wildcards to subscribe to multiple topics simultaneously. A wildcard can only be used to subscribe to topics, not to publish a message. There are two different kinds of wildcards: _single-level and _multi-level.
Single Level: +
As the name suggests, a single-level wildcard replaces one topic level. The plus symbol represents a single-level wildcard in a topic.
Any topic matches a topic with single-level wildcard if it contains an arbitrary string instead of the wildcard. For example a subscription to _myhome/groundfloor/+/temperature can produce the following results:
Multi Level: #
The multi-level wildcard covers many topic levels. The hash symbol represents the multi-level wild card in the topic. For the broker to determine which topics match, the multi-level wildcard must be placed as the last character in the topic and preceded by a forward slash.
When a client subscribes to a topic with a multi-level wildcard, it receives all messages of a topic that begins with the pattern before the wildcard character, no matter how long or deep the topic is. If you specify only the multi-level wildcard as a topic (_#), you receive all messages that are sent to the MQTT broker. If you expect high throughput, subscription with a multi-level wildcard alone is an anti-pattern (see the best practices below).
Topics beginning with $
Generally, you can name your MQTT topics as you wish. However, there is one exception: __ Topics that start with a $ symbol have a different purpose.__ These topics are not part of the subscription when you subscribe to the multi-level wildcard as a topic (#). The $-symbol topics are reserved for internal statistics of the MQTT broker. Clients cannot publish messages to these topics. At the moment, there is no official standardization for such topics. Commonly, $SYS/ is used for all the following information, but broker implementations varies. One suggestion for $SYS-topics is in the MQTT GitHub wiki. Here are some examples:
These are the basics of MQTT message topics. As you can see, MQTT topics are dynamic and provide great flexibility. When you use wildcards in real-world applications, there are some challenges you should be aware of. We have collected the best practices that we have learned from working extensively with MQTT in various projects and are always open to suggestions or a discussion about these practices. Use the comments to start a conversation, Let us know your best practices or if you disagree with one of ours!
Never use a leading forward slash
A leading forward slash is permitted in MQTT. For example, /myhome/groundfloor/livingroom. However, the leading forward slash introduces an unnecessary topic level with a zero character at the front. The zero does not provide any benefit and often leads to confusion.
Never use spaces in a topic
A space is the natural enemy of every programmer. When things are not going the way they should, spaces make it much harder to read and debug topics. As with leading forward slashes, just because something is allowed, doesn’t mean it should be used. UTF-8 has many different white space types, such uncommon characters should be avoided.
Keep the topic short and concise
Each topic is included in every message in which it is used. Make your topics as short and concise as possible. When it comes to small devices, every byte counts and topic length has a big impact.
Use only ASCII characters, avoid non printable characters
Because non-ASCII UTF-8 characters often display incorrectly, it is very difficult to find typos or issues related to the character set. Unless it is absolutely necessary, we recommend avoiding the use of non-ASCII characters in a topic.
Embed a unique identifier or the Client Id into the topic
It can be very helpful to include the unique identifier of the publishing client in the topic. The unique identifier in the topic helps you identify who sent the message. The embedded ID can be used to enforce authorization. Only a client that has the same client ID as the ID in the topic is allowed to publish to that topic. For example, a client with the _client1 ID is allowed to publish to _client1/status, but not permitted to publish to _client2/status.
Don’t subscribe to #
Sometimes, it is necessary to subscribe to all messages that are transferred over the broker. For example, to persist all messages into a database. Do not subscribe to all messages on a broker by using an MQTT client and subscribing to a multi-level wildcard. Frequently, the subscribing client is not able to process the load of messages that results from this method (especially if you have a massive throughput). Our recommendation is to implement an extension in the MQTT broker. For example, with the plugin system of HiveMQ you can hook into the behavior of HiveMQ and add an asynchronous routine to process each incoming message and persist it to a database.
Don’t forget extensibility
Topics are a flexible concept and there is no need to preallocate them in any way. However, both the publisher and the subscriber need to be aware of the topic. It is important to think about how topics can be extended to allow for new features or products. For example, if your smart-home solution adds new sensors, it should be possible to add these to your topic tree without changing the whole topic hierarchy.
Use specific topics, not general ones
When you name topics, don’t use them in the same way as in a queue. Be as specific topics as possible. For example, if you have three sensors in your living room, create topics for _myhome/livingroom/temperature, _myhome/livingroom/brightness and _myhome/livingroom/humidity. Do not send all values over _myhome/livingroom. Use of a single topic for all messages is a anti pattern. Specific naming also makes it possible for you to use other MQTT features such as retained messages. For more on retained messages, see part 8 of the Essentials series.
That brings us to the end of part five in our MQTT Essentials series. Thanks for reading. In the next post we cover Quality of Service (QoS) in MQTT. We’ll explain why this is an essential feature and how you can leverage it.
Have a great week and we look forward to seeing you for the next MQTT Monday!
If you would like to have our newsletter delivered directly to your inbox as soon as it is released, enter your email in the subscription form below. If you prefer RSS, you can subscribe to our RSS feed here.