In Introduction to MQTT Publish-subscribe Pattern, we learned that we need to initiate a subscription with the server to receive corresponding messages from it. The topic filter specified when subscribing determines which topics the server will forward to us, and the subscription options allow us to customize the forwarding behavior of the server further.
In this article, we will focus on exploring the available subscription options in MQTT and their usage.
A subscription in MQTT consists of a topic filter and corresponding subscription options. So, we can set different subscription options for each subscription.
MQTT 5.0 introduces four subscription options: QoS, No Local, Retain As Published, and Retain Handling. On the other hand, MQTT 3.1.1 only provides the QoS subscription option. However, the default behavior of these new subscription options in MQTT 5.0 remains consistent with MQTT 3.1.1. This makes it user-friendly if you plan to upgrade from MQTT 3.1.1 to MQTT 5.0.
Now, let’s explore the functions of these subscription options together.
QoS is the most commonly used subscription option, which represents the maximum QoS level that the server can use when sending messages to the subscriber.
A client may specify a QoS level below 2 during subscription if its implementation does not support QoS 1 or 2.
Additionally, if the server’s maximum supported QoS level is lower than the QoS level requested by the client during the subscription, it becomes apparent that the server cannot meet the client’s requirements. In such cases, the server informs the subscriber of the granted maximum QoS level through the subscription response packet (SUBACK). The subscriber can then assess whether to accept the granted QoS level and continue communication.
A simple calculation formula:
The maximum QoS granted by the server = min ( The maximum QoS supported by the server, The maximum QoS requested by the client )
However, the maximum QoS level requested during subscription does not restrict the QoS level used by the publishing end when sending messages. When the requested maximum QoS level during subscription is lower than the QoS level used for message publishing, the server will not ignore these messages. To maximize message delivery, it will downgrade the QoS level of these messages before forwarding.
Similarly, we have a simple calculation formula:
QoS in the forwarded message = min ( The original QoS of the message, The maximum QoS granted by the server )
The No Local option has only two possible values, 0 and 1. A value of 1 indicates that the server must not forward the message to the client that published it, while 0 means the opposite.
This option is commonly used in bridging scenarios. Bridging is essentially two MQTT Servers establishing an MQTT connection, then they subscribe to some topics from each other. The server forwards client messages to another server, which can continue forwarding them to its clients.
Let’s consider the most straightforward example where we assume two MQTT servers, Server A and Server B.
They have subscribed to the topic
# from each other. Now, Server A forwards some messages from the client to Server B, and when Server B looks for a matching subscription, Server A is there too. If Server B forwards the messages to Server A, then Server A will forward them to Server B again after receiving them, thus falling into an endless forwarding storm.
However, if both Server A and Server B set the No Local option to 1 while subscribing to the topic
#, this problem can be ideally avoided.
Retain As Published
The Retain As Published option also has two possible values, 0 and 1. Setting it to 1 means the server should preserve the Retain flag unchanged when forwarding application messages to subscribers, and setting it to 0 means that the Retain flag must be cleared.
Like the No Local option, Retain As Published primarily applies in bridge scenarios.
We know that when the server receives a retained message, in addition to storing it, it will also forward it to existing subscribers like a normal message, and the Retain flag of the message will be cleared when forwarding.
This presents a challenge in bridge scenarios. Continuing with the previous setup, when Server A forwards a retained message to Server B, the Retain flag is cleared, causing Server B not to recognize it as a retained message and not store it. This makes retained messages unusable across bridges.
In MQTT 5.0, we can let the bridged server set the “Retain” publish option to 1 when subscribing to solve this problem.
The Retain Handling subscription option indicates to the server whether to send retained messages when a subscription is established.
When a subscription is established, the retained messages matching the subscription in the server will be delivered by default.
However, there are cases where a client may not want to receive retained messages. For example, if a client reuses a session during connection but cannot confirm whether the previous connection successfully created the subscription, it may attempt to subscribe again. If the subscription already exists, the retained messages may have been consumed, or the server may have cached some messages that arrived during the client’s offline period. In such cases, the client may not want to receive the retained messages the server publishes.
Additionally, the client may not want to receive the retained message at any time, even during the initial subscription. For example, we send the state of the switch as a retained message, but for a particular subscriber, the switch event will trigger some operations, so it is helpful not to send the retained message in this case.
We can choose among these three different behaviors using Retain Handling:
- Setting Retain Handling to 0 means that retained messages are sent whenever a subscription is established.
- Setting Retain Handling to 1 means retained messages are sent only when establishing a new subscription, not a repeated one.
- Setting Retain Handling to 2 means no retained messages are sent when a subscription is established.