Bridges
Forward topics between this broker and others: from an edge site to a central broker, from a cloud broker back to devices, or between data centres.
A first bridge
A site broker forwarding its sensor readings to a central broker, and receiving commands from it:
[[bridges]] name = "central" host = "central.example.com" port = 1883 username = "site1" password = "secret" # Readings go out [[bridges.topics]] pattern = "sensors/#" direction = "out" qos = 1 # Commands come in [[bridges.topics]] pattern = "commands/#" direction = "in" qos = 1
The bridge connects to the central broker as an MQTT client with the id mercurio-bridge-central, or client_id when set. Each bridge needs its own name and client id, or the two would keep taking over each other's session; with notifications on, the client id is a level of the state topic, so it can't hold /, + or #. Messages published here on sensors/# are published there, and messages published there on commands/# are published here.
Delivery
A bridge forwards with up to 100 messages in flight in each direction, within the Receive Maximum of the broker receiving them, so a long link doesn't cost a round trip per message. Messages keep their order in each direction. QoS 1 and 2 messages are delivered at least once: see below for what happens when a connection drops.
Topics and prefixes
Each [[bridges.topics]] entry forwards pattern out (the default), in or both ways. The prefixes place the pattern in each broker's topic tree, so topics are renamed on the way:
[[bridges.topics]] pattern = "sensors/#" direction = "out" local_prefix = "site/" remote_prefix = "plants/plant1/"
| This broker | Remote broker |
|---|---|
site/sensors/t1 | plants/plant1/sensors/t1 |
With MQTT 5, retained messages keep their retain flag across the bridge, and properties such as user properties and response topics travel with the message. MQTT 3.1.1 carries neither; there, only the retained messages a broker sends when the bridge subscribes arrive as retained.
Both directions without loops
With direction = "both", a message forwarded one way could come back the other. With MQTT 5, the bridge subscribes with No Local, so the brokers never send it its own messages. A message crosses once, or twice when a connection drops while it is under way.
MQTT 3.1.1 brokers
[[bridges]] name = "legacy" host = "old-broker.example.com" protocol_version = "mqttv311"
MQTT 3.1.1 has no No Local, so a remote broker sends back the messages the bridge forwarded to it. The bridge remembers the last 1,000 messages it forwarded and drops those copies. An identical message published on the remote broker just after one was forwarded is dropped too.
Failover
[[bridges]] name = "cloud" addresses = ["broker1.example.com:8883", "broker2.example.com:8883"]
When a broker can't be reached or the connection is lost, the bridge waits and tries the next address, round and round. The first wait is about a second; each failed attempt doubles it, up to a minute, and a randomised share keeps bridges that lost the same broker from all coming back at once. A successful connection starts the waits over.
TLS and client certificates
[[bridges]] name = "cloud" host = "iot.example.com" port = 8883 [bridges.tls] enabled = true # Verify the remote broker with this CA instead of the system's roots ca_path = "/etc/mercurio/cloud-ca.crt" # Authenticate with a client certificate cert_path = "/etc/mercurio/site1.crt" key_path = "/etc/mercurio/site1.key"
enabled = true alone verifies the remote broker with the system's root certificates. A ca_path or a client certificate turns TLS on even without enabled, as --cafile does for recado and mercurioctl, so the password never crosses the network in the clear by mistake. cert_path and key_path go together; the broker refuses to start with only one. The files are loaded at startup and by mercuriod --check, and a file that can't be read or holds no certificate or key stops the broker from starting; each connection to the remote broker reads them again.
Monitoring
With notifications (on by default), the bridge publishes its state, retained, on this broker: 1 while forwarding, 0 at startup and whenever either connection is lost. It goes to $SYS/broker/connection/<client id>/state, or to notification_topic when set.
recado sub -t '$SYS/broker/connection/+/state' -T
# $SYS/broker/connection/mercurio-bridge-cloud/state: 1
With notifications_local_only = false, the remote broker gets the state on the same topic: the bridge publishes 1, retained, once connected, and connects with a retained will of 0, which the remote broker publishes when the link drops. Brokers that refuse client publishes to $SYS, Mercurio among them, need a notification_topic outside it:
[[bridges]] name = "cloud" host = "iot.example.com" notification_topic = "bridges/site1/state" notifications_local_only = false
mercurioctl bridge list, or the listBridges admin command, shows each bridge's link, the messages queued for it, the messages forwarded each way since the broker started, and those dropped because the other broker refused them or no route covered them.
When the remote broker is down
- Messages to forward wait in the bridge's queue on this broker, up to
max_queued_messages, and are sent once the bridge reconnects. - A QoS 1 or 2 message is acknowledged to the broker it came from only once the other broker accepted it. Messages under way when a connection drops are sent again over the next one, so none is lost; one may arrive twice. A message the receiving broker refuses, for instance for want of permission, is logged and dropped.
- The bridge's session on the remote broker lasts a day, so messages for
intopics wait there across short outages;session_expirysets another number of seconds, andclean_start = truestarts a new session at each connection instead.keepalivesets the keep-alive with the remote broker, 60 seconds by default. - The bridge retries forever; the broker starts even if every remote broker is down.
- If this broker closes the bridge's own connection, for instance when another client takes its client id, the bridge connects again.
Access control
The bridge connects to this broker in-process, so this broker's authentication and ACLs don't apply to it, and it may publish anywhere. On the remote broker, it is an ordinary client with the given credentials.