Skip to content
Start free evaluation

Publish/subscribe and topics

In MQTT, the sender and the receiver of a message never connect to each other. Every participant is a client that connects to a broker. This page is based on MQTT 5.0 §4.7 (topic names and topic filters) and §4.8 (subscriptions).

A publisher sends each message to the broker with a topic attached. A subscriber registers the topics it wants with a topic filter. The broker compares the topic of every incoming message with the filters of all subscriptions and forwards the message to each matching subscriber.

Publisher Broker Subscribers
┌──────────────┐ PUBLISH ┌──────────────────┐ PUBLISH ┌─────────────────────────┐
│ line 1 cell 2│ ───────▶ │ match topic │ ───────▶ │ factory/+/temperature │
└──────────────┘ │ against filters │ └─────────────────────────┘
topic: │ │ PUBLISH ┌─────────────────────────┐
factory/line1/temperature│ │ ───────▶ │ factory/# │
└──────────────────┘ └─────────────────────────┘

This model decouples the two sides in three ways:

  • Space: publishers and subscribers don’t know each other’s addresses. Both only need the broker address.
  • Number: one message reaches zero, one or many subscribers. The publisher sends it once, however many subscribers there are.
  • Time: with sessions and retained messages, a subscriber can receive messages that were published while it was offline. Sessions, keep alive and Will and QoS and retained messages explain how.

A client opens one TCP connection to the broker and starts a session with a CONNECT packet. It can publish and subscribe over the same connection.

  • Client Identifier: the string that identifies a session in the broker (§3.1.3.1). When another client connects with the same identifier, the broker closes the existing connection (§3.1.4).
  • Ports: IANA registered TCP port 1883 for plain MQTT and 8883 for MQTT over TLS (§4.2).
  • What the broker does: it manages connections and sessions, matches filters, enforces the delivery guarantees of each QoS, stores retained messages, and authenticates and authorizes clients.

A topic name is the UTF-8 string in a PUBLISH packet.

  • A / separates topic levels, for example factory/line1/temperature.
  • Topic names are case-sensitive: Factory and factory are different topics.
  • An empty level is still a level: /factory has two levels, an empty one and factory.
  • A topic name can’t contain the wildcard characters + and #, or the null character (U+0000).
  • A topic name is at most 65,535 bytes long.
  • Topics that start with $ are reserved for the broker (§4.7.2). Mosquitto publishes its own statistics under $SYS/.

A topic filter goes into a SUBSCRIBE packet. Filters can contain wildcards (§4.7.1).

Wildcard Matches Rule
+ Exactly one level It must fill the whole level; line+ is not a valid filter.
# Zero or more levels It must be the last level; factory/# also matches factory itself.
Filter factory/line1/temperature factory/line1/cell2/temperature factory
factory/+/temperature Match No match No match
factory/# Match Match Match
factory/+/+/temperature No match Match No match
+/line1/# Match Match No match

A filter that starts with # or + doesn’t match topics that start with $. To watch the $SYS topics, subscribe to them explicitly, for example with $SYS/#.

MQTT 5.0 adds shared subscriptions with the form $share/<group name>/<filter> (§4.8.2). Subscribers in the same group take turns receiving the matching messages, which lets several consumers share the load.

Once a topic tree is in use, publishers, subscribers and access rules all depend on it. Follow these guidelines:

  1. Go from general to specific, for example <site>/<line>/<cell>/<data kind>. Wildcards can then select any branch.
  2. Don’t start with /. It adds an empty first level that carries no meaning.
  3. Use ASCII letters, digits, - and _ only. Tools handle spaces and non-ASCII characters differently.
  4. Keep values out of topics. Timestamps and measurements belong in the payload. A growing set of topics costs broker resources and leaves retained messages behind.
  5. Put data, commands and status on separate branches. You can then grant access per branch.
  6. Use stable identifiers. An asset number is a better level than a device name that might change.

OPC 10000-14 §7.3.4 defines this conventional structure for PubSub topics:

opcua/<encoding>/<message type>/<PublisherId>/<WriterGroup name>[/<DataSetWriter name>]

For example, UADP data from the line1 WriterGroup of PublisherId 100 uses the topic opcua/uadp/data/100/line1. The PDComm tutorials follow this convention.