· MQTT· Mosquitto· EMQX· clustering· høj tilgængelighed· IoT· broker· TLS· failover
MQTT broker clustering og høj tilgængelighed — Mosquitto og EMQX
MQTT broker HA-design: Mosquitto bridge-konfiguration, EMQX clustering, load balancing, session persistens, failover, TLS og IoT gateway-integration.
Af M-Bus Gateway
En MQTT broker er singlepoint-of-failure i IoT-arkitekturer. Her er design-mønstrene for høj tilgængelighed.
Mosquitto bridge-konfiguration
Mosquitto har ingen native clustering.
HA-løsning: Bridge-mode — to Mosquitto-instanser synkroniserer topics.
Topologi:
Gateway → Mosquitto Primary (Hetzner)
↕ bridge
Mosquitto Secondary (Backup VPS)
↕
Server (subscriber)
# /etc/mosquitto/conf.d/bridge.conf (på Primary)
connection backup-broker
address backup-broker.example.com:8883
bridge_cafile /etc/mosquitto/certs/ca.crt
bridge_certfile /etc/mosquitto/certs/primary.crt
bridge_keyfile /etc/mosquitto/certs/primary.key
topic meters/+/data both 1
topic meters/+/status both 1
topic meters/+/alarm both 1
# cmd og ota bridgedes IKKE — sendes direkte til aktiv broker
cleansession false
restart_timeout 5
bridge_attempt_unsubscribe false
EMQX clustering (anbefalet til produktion)
# docker-compose.yml — EMQX 5.x cluster
services:
emqx-1:
image: emqx:5.4
environment:
EMQX_NODE__NAME: "emqx@emqx-1.mbus-internal"
EMQX_CLUSTER__DISCOVERY_STRATEGY: static
EMQX_CLUSTER__STATIC__SEEDS: "[emqx@emqx-1.mbus-internal,emqx@emqx-2.mbus-internal]"
EMQX_LISTENERS__SSL__DEFAULT__BIND: "0.0.0.0:8883"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__CACERTFILE: "/etc/emqx/certs/ca.crt"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__CERTFILE: "/etc/emqx/certs/server.crt"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__KEYFILE: "/etc/emqx/certs/server.key"
EMQX_LISTENERS__SSL__DEFAULT__SSL_OPTIONS__VERIFY: verify_peer
volumes:
- emqx-1-data:/opt/emqx/data
- ./certs:/etc/emqx/certs:ro
networks:
internal:
aliases: [emqx-1.mbus-internal]
emqx-2:
image: emqx:5.4
environment:
EMQX_NODE__NAME: "emqx@emqx-2.mbus-internal"
EMQX_CLUSTER__DISCOVERY_STRATEGY: static
EMQX_CLUSTER__STATIC__SEEDS: "[emqx@emqx-1.mbus-internal,emqx@emqx-2.mbus-internal]"
volumes:
- emqx-2-data:/opt/emqx/data
- ./certs:/etc/emqx/certs:ro
networks:
internal:
aliases: [emqx-2.mbus-internal]
haproxy:
image: haproxy:2.9
ports:
- "8883:8883"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
depends_on: [emqx-1, emqx-2]
volumes:
emqx-1-data:
emqx-2-data:
# haproxy.cfg — TCP load balancing til EMQX
frontend mqtt_tls
bind *:8883
mode tcp
default_backend emqx_nodes
backend emqx_nodes
mode tcp
balance leastconn # Mindst connections — ikke round-robin
option tcp-check
timeout connect 5s
timeout server 300s # Lang timeout — MQTT keep-alive
server emqx-1 emqx-1.mbus-internal:8883 check inter 10s rise 2 fall 3
server emqx-2 emqx-2.mbus-internal:8883 check inter 10s rise 2 fall 3 backup
Session persistens ved failover
# gateway/src/mqtt/client.py
import paho.mqtt.client as mqtt
def create_ha_client(gateway_id: str, brokers: list[str]) -> mqtt.Client:
"""
MQTT client med automatisk failover til backup broker.
clean_session=False: Broker husker subscriptions.
"""
client = mqtt.Client(
client_id=f"gw-{gateway_id}",
clean_session=False, # Persistent session — data mistes ikke
protocol=mqtt.MQTTv311,
)
client.will_set(
topic=f"meters/{gateway_id}/status",
payload='{"online":false}',
qos=1,
retain=True,
)
# Primary broker forsøges først
primary = brokers[0]
client.connect(
host=primary,
port=8883,
keepalive=60,
)
return client
def on_disconnect(client, userdata, rc):
"""Reconnect automatisk ved disconnect."""
if rc != 0: # Uventet disconnect
import time
backoff = 1
for broker in userdata["brokers"]:
try:
client.connect(broker, port=8883, keepalive=60)
return
except Exception:
time.sleep(backoff)
backoff = min(backoff * 2, 60)
ACL og isolering
# /etc/mosquitto/acl.conf — Per-gateway ACL
# Gateway kan kun publicere til egne topics:
pattern readwrite meters/%u/#
# Server subscriber kan læse alle gateways:
user server-subscriber
topic read meters/+/data
topic read meters/+/status
topic read meters/+/alarm
# Server kan skrive kommandoer til alle gateways:
user server-commander
topic write meters/+/cmd
topic write meters/+/ota
Monitoring: broker-metrik
# EMQX metrics API:
curl http://emqx-1:18083/api/v5/metrics \
-H "Authorization: Bearer <api-key>" | jq '{
connections: .connections.count,
messages_received: ."messages.received",
messages_sent: ."messages.sent",
subscriptions: .subscriptions.count
}'
# Eksempel output:
# {
# "connections": 127,
# "messages_received": 48293,
# "messages_sent": 48291,
# "subscriptions": 255
# }
# Mosquitto: Abonnér på $SYS topics:
mosquitto_sub -h localhost -t '$SYS/broker/#' -v
# $SYS/broker/clients/connected 127
# $SYS/broker/messages/received 48293
# $SYS/broker/load/messages/received/1min 84.23
Gateway failover-test
# Test failover: Stop primary broker, verificér gateway reconnect
# Stop primary:
docker compose stop emqx-1
# Overvåg gateway log:
sudo journalctl -u mbus-gateway -f | grep -E "connected|disconnect|reconnect"
# Forventet output:
# 2026-05-24 06:00:01 broker_disconnected broker=emqx-1.mbus-internal
# 2026-05-24 06:00:02 reconnecting_to_broker broker=emqx-2.mbus-internal
# 2026-05-24 06:00:03 broker_connected broker=emqx-2.mbus-internal
# Data tab under failover:
# QoS 0: Op til ét telegram kan mistes
# QoS 1: Ingen tab — delivery confirmed inden ACK
# Platform bruger QoS 1 for daglig datapayload
Mosquitto vs EMQX sammenligning
Mosquitto 2.x EMQX 5.x
───────────────────────────────────────────────
Native clustering: ❌ (bridge-mode) ✅ (Mnesia cluster)
Max connections: ~100.000 1.000.000+
Dashboard UI: ❌ ✅ (port 18083)
HTTP API: ❌ ✅ (REST management)
Rule engine: ❌ ✅ (stream processing)
Docker image size: ~20MB ~200MB
Memory (idle): ~10MB ~200MB
Pris: Gratis Gratis (open-source)
M-Bus Gateway valg: Mosquitto
Årsag: 127 gateways × 1 connection = minimal load.
Mosquitto er tilstrækkelig og har lavt resource-footprint.
EMQX anbefales ved 1.000+ gateways eller behov for rule engine.
Konklusion
Mosquitto er tilstrækkelig til under 1.000 gateways og kan HA-konfigureres med bridge-mode. EMQX 5.x giver native clustering og dashboard til større deployments. clean_session=False er afgørende — det sikrer at broker husker subscriptions og queuer beskeder ved kortvarig disconnect. HAProxy med leastconn load-balancer fordeler forbindelser jævnt.