Zum Hauptinhalt springen
Alle Artikel
RabbitMQMessage BrokerHochverfügbarkeitArchitektur

RabbitMQ in der Industrie: Warum eine Queue allein noch keine ausfallsichere Datenautobahn ist

RabbitMQ ist mehr als eine Queue. Dieser Artikel erklärt Quorum Queues, Publisher Confirms, Consumer Acknowledgements, At-least-once Delivery und Idempotenz – anhand konkreter Industriearchitekturen.

20 Min. Lesezeit

Eine Maschine liefert einen Messwert.

Die Software verarbeitet ihn.

Fertig.

Zumindest klingt die Architektur auf dem Papier so einfach.

In einer kleinen Anwendung mit einer Maschine und einem Empfänger kann das tatsächlich funktionieren. Die Anwendung verbindet sich direkt mit der Maschine, liest Daten aus und schreibt diese anschließend in eine Datenbank.

Doch Produktionssysteme bleiben selten so klein.

Plötzlich liefern nicht mehr eine, sondern 50 Maschinen Daten. Neben der Produktionssoftware benötigen auch MES, Qualitätssicherung, Historisierung und Dashboard dieselben Informationen. Gleichzeitig entstehen Alarme, Zustandsänderungen und Produktionsereignisse.

Und dann fällt für einige Minuten die Datenbank aus.

Was passiert jetzt mit den Daten?

Genau an diesem Punkt wird aus einer einfachen Schnittstelle ein Architekturproblem.

Eine Möglichkeit, dieses Problem zu lösen, sind Message Broker wie RabbitMQ.

Doch auch hier gilt:

Nur weil zwischen Maschine und Anwendung plötzlich eine Queue liegt, ist das System noch lange nicht skalierbar oder ausfallsicher.

Das eigentliche Problem beginnt mit direkten Verbindungen

Nehmen wir eine einfache Produktionsanlage.

Eine Maschine liefert Produktionsdaten an eine Anwendung:

Maschine ─────► Produktionssoftware

Solange beide Systeme verfügbar sind und ungefähr mit derselben Geschwindigkeit arbeiten, gibt es kaum Probleme.

Nun soll zusätzlich ein Dashboard die Daten anzeigen:

              ┌──► Produktionssoftware
Maschine ─────┤
              └──► Dashboard

Danach interessiert sich die Qualitätssicherung für dieselben Werte:

              ┌──► Produktionssoftware
Maschine ─────┼──► Dashboard
              └──► Qualitätssicherung

Und irgendwann kommen hinzu:

  • Historisierung
  • MES
  • ERP
  • Alarmierung
  • Reporting
  • Predictive Maintenance
  • Data Analytics
  • Cloud-Anwendungen

Aus einer einfachen Verbindung wird eine Landschaft aus gegenseitigen Abhängigkeiten.

Jedes System muss wissen, wo die Daten verfügbar sind. Jeder Ausfall eines Empfängers muss behandelt werden. Und jeder neue Empfänger verändert möglicherweise die bestehende Architektur.

Noch problematischer wird es, wenn Daten synchron übertragen werden. Die Maschine oder der vorgelagerte Service sendet einen Datensatz und wartet darauf, dass das Zielsystem ihn verarbeitet.

Ist dieses Ziel langsam, wird auch der Sender langsam.

Ist es nicht erreichbar, entsteht ein Fehler.

Damit hängt plötzlich die Geschwindigkeit der Datenerfassung von der Geschwindigkeit der nachgelagerten Systeme ab.

Was ein Message Broker daran verändert

Ein Message Broker setzt genau zwischen diesen beiden Seiten an.

Der Produzent schickt seine Nachricht nicht mehr direkt an die Anwendung, die sie später verarbeiten soll. Er übergibt sie zunächst an den Broker.

Maschine
   │
   ▼
Treiber / Edge-Service
   │
   ▼
RabbitMQ
   │
   ▼
Verarbeitung

Damit entstehen zwei voneinander getrennte Vorgänge:

  1. Eine Nachricht wird erzeugt.
  2. Eine Nachricht wird verarbeitet.

Diese Entkopplung ist einer der wichtigsten Vorteile einer Messaging-Architektur.

Der Produzent muss nicht darauf warten, dass eine Datenbank gerade schnell genug schreibt oder ein Analysealgorithmus seine Berechnung abgeschlossen hat.

Er muss im Wesentlichen nur sicherstellen, dass die Nachricht zuverlässig an die Messaging-Infrastruktur übergeben wurde. Die Verarbeitung kann danach unabhängig stattfinden.

Die Queue wird zum Puffer

Das lässt sich gut an einem Lastsprung zeigen.

Angenommen, eine Produktionsanlage erzeugt normalerweise 200 Nachrichten pro Sekunde. Die nachgelagerte Verarbeitung schafft 300 Nachrichten pro Sekunde. Alles funktioniert problemlos.

Nun starten mehrere Produktionslinien gleichzeitig und für kurze Zeit entstehen 600 Nachrichten pro Sekunde. Eine synchrone Architektur kann jetzt unter Druck geraten.

Mit einer Queue sieht das anders aus:

Produktion
600 msg/s
    │
    ▼
┌───────────────┐
│     Queue     │
│ ▪ ▪ ▪ ▪ ▪ ▪ ▪ │
└───────────────┘
    │
    ▼
Verarbeitung
300 msg/s

Die Queue nimmt kurzfristig mehr Nachrichten auf, als verarbeitet werden können. Sinkt die Produktionsrate später wieder, arbeitet der Consumer den Rückstand ab.

Der Message Broker wirkt damit wie ein Puffer zwischen unterschiedlich schnellen Systemen. Gerade bei industriellen Anwendungen ist das interessant, weil Last häufig nicht gleichmäßig entsteht.

Produktionsstarts, Chargenwechsel, Schichtwechsel oder das Wiederanlaufen einer Anlage können kurzfristig deutlich höhere Datenmengen erzeugen.

RabbitMQ ist mehr als eine Warteschlange

RabbitMQ wird häufig mit einer Queue gleichgesetzt. Die eigentliche Architektur besitzt jedoch mehrere Komponenten. Vereinfacht:

Producer
   │
   ▼
Exchange
   │
   ├────────► Queue A ─────► Consumer A
   │
   ├────────► Queue B ─────► Consumer B
   │
   └────────► Queue C ─────► Consumer C

Der Producer erzeugt eine Nachricht. Eine Exchange entscheidet anhand der definierten Bindings und Routing-Regeln, wohin diese Nachricht weitergeleitet wird. Eine oder mehrere Queues speichern beziehungsweise puffern die Nachricht für die jeweiligen Consumer. Die Consumer verarbeiten die Nachrichten.

Diese Trennung ermöglicht interessante Architekturen. Ein Produktionsereignis kann beispielsweise gleichzeitig an verschiedene Systeme verteilt werden:

production.completed
         │
         ▼
      Exchange
         │
     ┌───┼─────────┐
     ▼   ▼         ▼
    MES  QS   Historisierung

Die Maschine beziehungsweise der erzeugende Service muss diese Systeme nicht kennen. Er meldet lediglich:

PRODUCTION_COMPLETED

Wer sich dafür interessiert, wird auf der Messaging-Seite konfiguriert.

Damit beginnt Event-driven Architecture

Dieses Prinzip wird häufig als Event-driven Architecture bezeichnet. Anstatt andere Systeme direkt aufzurufen, beschreibt eine Anwendung, was passiert ist. Beispielsweise:

DEVICE_CONNECTED

WEIGHT_MEASURED

ORDER_STARTED

PRODUCTION_COMPLETED

ALARM_RAISED

Das Ereignis wird veröffentlicht. Andere Systeme können darauf reagieren.

Das erzeugende System muss aber nicht wissen, welche Anwendungen das sind. Ein neues Analytics-System kann später hinzugefügt werden, ohne dass die Maschine oder der ursprüngliche Service angepasst werden muss.

Das ist ein fundamentaler Unterschied zur klassischen Punkt-zu-Punkt-Integration.

Ein Ereignis ist etwas anderes als ein Befehl

Bei einer sauberen Messaging-Architektur sollte außerdem zwischen Commands und Events unterschieden werden.

Ein Command fordert jemanden auf, etwas zu tun:

TARE_SCALE

START_ORDER

RESET_DEVICE

Ein Event beschreibt dagegen etwas, das bereits passiert ist:

SCALE_TARED

ORDER_STARTED

DEVICE_DISCONNECTED

Damit ergeben sich unterschiedliche Kommunikationsrichtungen.

Zentrales System
      │
      │ Command
      ▼
    Gerät
      │
      │ Event
      ▼
Messaging-Infrastruktur

Diese Unterscheidung wirkt zunächst akademisch. In größeren verteilten Systemen hilft sie aber enorm dabei, Verantwortlichkeiten sauber zu definieren.

Doch eine Queue allein löst noch kein Ausfallproblem

Nun kommt einer der häufigsten Denkfehler bei Messaging-Systemen. Es wird eine Queue eingeführt und damit gilt das System plötzlich als ausfallsicher.

Aber was passiert, wenn genau der Server ausfällt, auf dem diese Queue liegt?

Producer
   │
   ▼
RabbitMQ
   X
 Serverausfall

Die Queue kann Daten puffern. Sie kann aber nur dann vor einem Serverausfall schützen, wenn auch die Messaging-Infrastruktur selbst entsprechend aufgebaut ist.

RabbitMQ unterscheidet deshalb verschiedene Queue- und Datentypen. Für hochverfügbare, replizierte Queues stehen insbesondere Quorum Queues zur Verfügung. Sie basieren auf dem Raft-Konsensalgorithmus und replizieren ihren Zustand über mehrere Mitglieder. Seit RabbitMQ 4.0 gibt es die früher verwendeten „Classic Mirrored Queues" nicht mehr; Quorum Queues und Streams sind die replizierten Datenstrukturen. (RabbitMQ)

Quorum Queues einfach erklärt

Nehmen wir drei RabbitMQ-Server:

RabbitMQ 1
RabbitMQ 2
RabbitMQ 3

Eine Quorum Queue kann auf diesen drei Nodes Replikate besitzen. Einer davon übernimmt die Rolle des Leaders.

             Quorum Queue
                  │
       ┌──────────┼──────────┐
       ▼          ▼          ▼
    Node 1      Node 2      Node 3
    Leader      Follower    Follower

Änderungen an der Queue werden zwischen den Mitgliedern abgestimmt. Der entscheidende Begriff dabei lautet: Quorum.

Ein Quorum bedeutet vereinfacht eine Mehrheit. Bei drei Mitgliedern sind dafür mindestens zwei erforderlich. Fällt ein einzelner Node aus, bleiben zwei übrig.

       ┌──────────┼──────────┐
       ▼          ▼          X
    Node 1      Node 2      Node 3

Die Mehrheit ist weiterhin vorhanden. Fällt der aktuelle Leader aus, kann ein anderes Mitglied zum Leader gewählt werden und die Queue ihre Arbeit fortsetzen. RabbitMQ empfiehlt aus diesem Grund ungerade Clustergrößen; ein Drei-Node-Cluster kann den Ausfall eines einzelnen Nodes verkraften und weiterhin ein Quorum bilden. (RabbitMQ)

Bei drei Replikaten gilt also vereinfacht:

3 verfügbar → funktioniert
2 verfügbar → funktioniert
1 verfügbar → kein Quorum

Hochverfügbarkeit bedeutet nicht, dass beliebig viele Komponenten ausfallen dürfen. Ein konsistenzorientiertes verteiltes System braucht weiterhin genügend Teilnehmer, um einen gültigen Zustand bestimmen zu können.

Und wann ist eine Nachricht wirklich sicher angekommen?

Selbst mit drei RabbitMQ-Servern gibt es noch eine weitere wichtige Frage.

Der Producer sendet eine Nachricht. Direkt danach bricht die Netzwerkverbindung ab. Hat RabbitMQ die Nachricht erhalten? Oder nicht? Aus Sicht des Producers ist das möglicherweise nicht eindeutig.

TCP allein löst dieses Problem auf Anwendungsebene nicht vollständig. RabbitMQ verwendet deshalb Publisher Confirms.

Producer
   │
   │ Nachricht
   ▼
RabbitMQ
   │
   │ Confirm
   ▼
Producer

Erst mit der Bestätigung weiß der Producer, dass RabbitMQ die Verantwortung für die Nachricht übernommen hat.

Bei einer Quorum Queue wird ein Publisher Confirm erst ausgegeben, nachdem die Nachricht erfolgreich auf ein Quorum der Queue-Mitglieder repliziert wurde. (RabbitMQ)

Das ist deutlich mehr als: „Der TCP-Socket hat keinen Fehler geliefert."

Auf der anderen Seite gibt es Acknowledgements

Dasselbe Problem existiert beim Consumer. RabbitMQ liefert eine Nachricht.

Der Consumer beginnt mit der Verarbeitung. Beispielsweise:

1. Nachricht empfangen
2. Daten validieren
3. Datensatz in PostgreSQL speichern
4. Nachricht bestätigen

Erst danach schickt der Consumer ein Acknowledgement.

RabbitMQ
   │
   │ Nachricht
   ▼
Consumer
   │
   │ Verarbeitung erfolgreich
   │
   └──── ACK ────► RabbitMQ

Nach dem Acknowledgement kann RabbitMQ die Nachricht entfernen. Stürzt der Consumer dagegen während der Verarbeitung ab, bevor er bestätigt hat, kann die Nachricht erneut zugestellt werden.

Publisher Confirms und Consumer Acknowledgements lösen dabei zwei unterschiedliche Seiten desselben Problems: die Übergabe vom Producer an RabbitMQ und die Übergabe von RabbitMQ an den Consumer. (RabbitMQ)

Genau dadurch können Nachrichten doppelt ankommen

Ein zuverlässiges Messaging-System muss damit rechnen, dass eine Nachricht mehr als einmal zugestellt wird.

RabbitMQ
   │
   ▼
Consumer
   │
   ├── Datensatz gespeichert
   │
   X Consumer stürzt ab

Der Consumer hat den Datensatz bereits gespeichert. Das ACK hat RabbitMQ jedoch nicht mehr erreicht. RabbitMQ weiß deshalb nicht, dass die Verarbeitung erfolgreich war.

Nach dem Neustart kann die Nachricht erneut zugestellt werden.

Damit erhält man das Prinzip: At-least-once Delivery. Die Nachricht wird mindestens einmal zugestellt – möglicherweise aber auch zweimal.

RabbitMQ weist ausdrücklich darauf hin, dass bei Verbindungs- oder Node-Ausfällen Redeliveries auftreten können und Consumer deshalb mit bereits gesehenen Nachrichten umgehen müssen. Auch ein Producer kann eine Nachricht erneut senden, wenn ihm ein Confirm verloren gegangen ist, obwohl RabbitMQ die ursprüngliche Nachricht bereits übernommen hatte. (RabbitMQ)

Deshalb ist Idempotenz so wichtig

Nehmen wir eine Nachricht:

{
  "messageId": "f7c6d914-...",
  "type": "PRODUCTION_COMPLETED",
  "orderId": "4711",
  "quantity": 500
}

Kommt diese Nachricht zweimal an, darf daraus nicht plötzlich entstehen:

produzierte Menge = 1000

Der Consumer muss erkennen, dass dieselbe fachliche Nachricht bereits verarbeitet wurde. Eine Möglichkeit ist eine eindeutige messageId.

Nachricht empfangen
      │
      ▼
messageId bereits verarbeitet?
      │
  ┌───┴───┐
  │       │
 ja      nein
  │       │
ignorieren verarbeiten

Noch besser ist es, Geschäftsoperationen selbst so zu gestalten, dass eine Wiederholung keinen unerwünschten Effekt besitzt. Dieses Prinzip nennt man Idempotenz.

Zuverlässigkeit entsteht nicht nur im Message Broker. Sie entsteht aus dem Zusammenspiel von Broker und Anwendungslogik.

„Exactly once" klingt einfacher, als es in verteilten Systemen ist

Auf den ersten Blick wäre die perfekte Lösung: Jede Nachricht wird exakt einmal verarbeitet.

Das Problem ist die Grenze zwischen verschiedenen Systemen. RabbitMQ kann wissen, ob eine Nachricht zugestellt wurde. Die Datenbank kann wissen, ob eine Transaktion gespeichert wurde. Aber zwischen diesen beiden Operationen kann jederzeit ein Fehler auftreten.

RabbitMQ → Consumer → Datenbank

Genau deshalb werden robuste Systeme häufig nach dem Prinzip gebaut:

At-least-once delivery
        +
idempotente Verarbeitung

statt darauf zu vertrauen, dass ein Fehler irgendwo zwischen Netzwerk, Broker, Anwendung und Datenbank niemals zum erneuten Zustellen führt.

Was passiert, wenn die Verarbeitung fehlschlägt?

Nicht jede Nachricht kann erfolgreich verarbeitet werden. Vielleicht ist der Datenbankserver gerade nicht verfügbar. Oder ein externer Dienst antwortet nicht. Dann kann ein Retry sinnvoll sein.

Anders sieht es bei einer dauerhaft fehlerhaften Nachricht aus:

{
  "weight": "Kartoffel"
}

Wenn die Anwendung eine Zahl erwartet, wird dieselbe Nachricht auch beim hundertsten Versuch nicht funktionieren. Wird sie einfach immer wieder in dieselbe Queue zurückgelegt, entsteht eine Endlosschleife.

Solche Nachrichten werden häufig als Poison Messages bezeichnet. Eine typische Architektur verwendet deshalb:

  • begrenzte Wiederholungsversuche,
  • Verzögerungen zwischen Retries,
  • Dead-Letter-Mechanismen,
  • Logging,
  • Alarmierung.

Aktuelle RabbitMQ-Versionen bieten für Quorum Queues zusätzliche Funktionen für Poison-Message-Handling und verzögerte Retries. (RabbitMQ)

Damit wird aus:

Fehler → sofort erneut versuchen → Fehler → sofort erneut ...

beispielsweise:

Fehler
  │
  ├── Retry 1
  │
  ├── Retry 2
  │
  ├── Retry 3
  │
  ▼
Dead Letter / Fehlerbehandlung

Skalieren bedeutet oft: mehr Consumer

Eine weitere Stärke einer Queue besteht darin, Verarbeitung horizontal zu skalieren. Angenommen, ein Consumer schafft 100 Nachrichten pro Sekunde, die Produktion liefert aber 300 Nachrichten pro Sekunde. Dann können mehrere Instanzen desselben Consumers gestartet werden.

              ┌──► Consumer 1
Queue ────────┼──► Consumer 2
              └──► Consumer 3

Jeder Consumer verarbeitet einen Teil der Nachrichten. Damit kann Verarbeitungskapazität erhöht werden, ohne die Produzenten ändern zu müssen. Steigt die Last, können zusätzliche Worker bereitgestellt werden. Sinkt die Last, kann ihre Anzahl wieder reduziert werden.

Aber horizontale Skalierung hat Auswirkungen auf die Reihenfolge

Nehmen wir drei Nachrichten:

1. ORDER_STARTED
2. PRODUCT_PRODUCED
3. ORDER_COMPLETED

Bei einem einzelnen Consumer ist deren Verarbeitung relativ leicht geordnet. Bei mehreren parallelen Consumern kann dagegen entstehen:

Consumer A → Nachricht 1
Consumer B → Nachricht 2
Consumer C → Nachricht 3

Die Laufzeit jeder Verarbeitung ist unterschiedlich. Nachricht 3 könnte deshalb fertig verarbeitet sein, bevor Nachricht 2 abgeschlossen wurde.

Mehr Parallelität ist nicht automatisch besser. Wenn Reihenfolge fachlich relevant ist, muss sie bewusst Teil der Architektur werden. Mögliche Ansätze sind:

  • Partitionierung nach Maschine,
  • Partitionierung nach Auftrag,
  • unterschiedliche Queues,
  • Single-Active-Consumer-Konzepte,
  • sequenzielle Verarbeitung bestimmter Ereignisse.

Die richtige Architektur hängt deshalb nicht nur davon ab, wie viele Nachrichten entstehen. Sie hängt auch davon ab, welche fachlichen Beziehungen zwischen diesen Nachrichten bestehen.

RabbitMQ oder MQTT?

Spätestens bei Industrie- und IoT-Projekten taucht häufig die Frage auf: RabbitMQ oder MQTT? Streng genommen ist diese Frage falsch gestellt.

RabbitMQ ist ein Message Broker beziehungsweise eine Messaging-Plattform. MQTT ist ein Kommunikationsprotokoll.

MQTT definiert ein leichtgewichtiges Publish/Subscribe-Modell mit Topics und verschiedenen Quality-of-Service-Stufen. MQTT 5 unterscheidet QoS 0 „at most once", QoS 1 „at least once" und QoS 2 „exactly once" auf Protokollebene. (OASIS Open)

RabbitMQ unterstützt verschiedene Messaging-Protokolle. Neben AMQP kann RabbitMQ auch MQTT über das mitgelieferte MQTT-Plugin bereitstellen. (RabbitMQ)

Eine mögliche Architektur ist deshalb sogar:

Sensor / Edge Device
        │
        │ MQTT
        ▼
     RabbitMQ
        │
        │ AMQP
        ▼
Backend Services

MQTT und RabbitMQ schließen sich also nicht gegenseitig aus. Die eigentliche Frage lautet eher: Welches Protokoll eignet sich für welche Verbindung und welche Broker-Funktionen benötigt die gesamte Architektur?

Für kleine Edge-Geräte oder Telemetrie kann MQTT sehr attraktiv sein. Für komplexere Backend-Workflows, unterschiedliche Routing-Szenarien, Worker-Queues, Commands und zuverlässige Verarbeitung können AMQP-basierte RabbitMQ-Architekturen wiederum sehr interessant sein.

Und was ist mit Millionen Maschinendaten pro Tag?

Bei industriellen Systemen entstehen schnell große Datenmengen. 200 Maschinen liefern jeweils einen Messwert pro Sekunde. Das ergibt:

200 Nachrichten pro Sekunde
12.000 Nachrichten pro Minute
720.000 Nachrichten pro Stunde
17.280.000 Nachrichten pro Tag

Das klingt zunächst enorm. Entscheidend ist aber nicht nur die Tagesmenge. Wichtiger sind Fragen wie:

  • Wie groß ist eine Nachricht?
  • Wie hoch ist die maximale Nachrichtenrate?
  • Wie viele Consumer gibt es?
  • Wie lange bleiben Nachrichten in Queues?
  • Müssen Nachrichten repliziert werden?
  • Muss die Historie erneut gelesen werden können?
  • Wie viele Routing-Ziele existieren?
  • Welche Verarbeitung passiert pro Nachricht?

Denn ein System mit 20 Millionen kleinen Nachrichten, die sofort verarbeitet werden, kann völlig andere Anforderungen haben als ein System mit fünf Millionen großen Nachrichten, die mehrere Tage im Broker verbleiben.

Queue oder Stream?

Eine klassische Queue beziehungsweise Quorum Queue ist typischerweise darauf ausgelegt, dass Nachrichten verarbeitet und anschließend entfernt werden.

Nachricht
   │
   ▼
Queue
   │
   ▼
Consumer
   │
   ▼
verarbeitet → entfernt

Manchmal möchte man Daten dagegen erneut lesen. Zum Beispiel:

  • ein neues Analytics-System soll die letzten sieben Tage neu berechnen,
  • ein Fehler in einem Algorithmus wurde behoben,
  • ein zweiter Consumer benötigt dieselbe historische Datenfolge,
  • sehr große Backlogs sollen effizient gespeichert werden.

Dafür bietet RabbitMQ Streams. Streams funktionieren eher wie ein fortlaufendes, persistentes Log:

1
2
3
4
5
6
7
8
────────────►

Consumer können an unterschiedlichen Positionen beginnen und Nachrichten erneut lesen. RabbitMQ beschreibt Streams ausdrücklich als Ergänzung zu Queues für Anwendungsfälle wie große Fan-outs, Replay, hohen Durchsatz und große Backlogs. Streams sind persistent und repliziert; über Super Streams lassen sie sich zusätzlich partitionieren. (RabbitMQ)

Deshalb sollte die Frage nicht lauten: „Wie bekommen wir möglichst viele Nachrichten in eine Queue?" Sondern: „Welches Messaging-Modell passt zu unseren Daten?"

Nicht jeder Messwert muss für immer durch RabbitMQ laufen

Eine skalierbare Architektur bedeutet nicht, dass jeder einzelne Sensorwert zwangsläufig dauerhaft gespeichert werden muss.

Nehmen wir einen Sensor, der 100 Werte pro Sekunde liefert. Für eine Maschinenregelung können alle 100 Werte relevant sein. Für ein Produktionsdashboard reicht vielleicht ein Wert pro Sekunde. Für einen Monatsreport vielleicht ein Mittelwert pro Minute.

100 Hz Rohdaten
      │
      ▼
Edge-Verarbeitung
      │
      ├──► 1 Hz Live-Daten
      │
      ├──► Ereignisse
      │
      └──► aggregierte Kennzahlen

Auch die beste Messaging-Infrastruktur ersetzt deshalb keine saubere Datenstrategie. Nur weil technisch Millionen Nachrichten transportiert werden können, bedeutet das nicht automatisch, dass jede dieser Nachrichten fachlich notwendig ist.

Was passiert bei einem kompletten RabbitMQ-Ausfall?

Eine hochverfügbare Messaging-Infrastruktur reduziert einzelne Fehlerpunkte. Sie beseitigt aber nicht jede mögliche Störung. Auch ein kompletter Cluster kann aufgrund von Netzwerkproblemen, Storage-Problemen, Fehlkonfigurationen, Ressourcenerschöpfung oder Wartungsfehlern zeitweise nicht erreichbar sein.

Deshalb muss auch der Producer entscheiden, was in diesem Fall passiert. Bei einem industriellen Edge-System kann beispielsweise lokal gepuffert werden:

Maschine
   │
   ▼
Edge Service
   │
   ├── RabbitMQ erreichbar ───► senden
   │
   └── RabbitMQ nicht erreichbar
             │
             ▼
        lokaler Puffer
             │
             ▼
        später nachsenden

Hochverfügbarkeit besteht aus mehreren Schichten. Nicht aus einem einzigen Produkt.

Genau deshalb reicht „Wir verwenden RabbitMQ" als Architekturentscheidung nicht

Für ein produktionsrelevantes Messaging-System müssen deutlich mehr Fragen beantwortet werden:

Producer

  • Was passiert, wenn RabbitMQ nicht erreichbar ist?
  • Werden Publisher Confirms verwendet?
  • Wie werden nicht bestätigte Nachrichten behandelt?
  • Können Nachrichten erneut gesendet werden?
  • Besitzt jede Nachricht eine eindeutige ID?

Broker

  • Welche Queue-Typen werden verwendet?
  • Müssen Nachrichten repliziert werden?
  • Wie viele Nodes gibt es?
  • Welche Queue-Limits gelten?
  • Wie werden Dead Letters behandelt?
  • Welche Monitoring-Metriken werden überwacht?

Consumer

  • Wann wird ein ACK gesendet?
  • Was passiert bei einem Verarbeitungsfehler?
  • Sind Consumer idempotent?
  • Gibt es Retries?
  • Wie werden Poison Messages behandelt?
  • Wie viele Nachrichten dürfen gleichzeitig verarbeitet werden?

Gesamtsystem

  • Muss Reihenfolge garantiert werden?
  • Dürfen Nachrichten doppelt verarbeitet werden?
  • Wie lange dürfen Systeme offline sein?
  • Wo wird gepuffert?
  • Welche Daten müssen historisiert werden?
  • Welche Daten dürfen verloren gehen – und welche keinesfalls?

Erst wenn diese Fragen beantwortet sind, entsteht aus einer Queue eine belastbare Messaging-Architektur.

Ein mögliches Muster für industrielle Anwendungen

Eine typische Architektur könnte beispielsweise so aussehen:

 Maschinen / Geräte
        │
        ▼
 Edge / Treiber
        │
        │ Events
        ▼
┌─────────────────────┐
│      RabbitMQ       │
│   3-Node-Cluster    │
│   Quorum Queues     │
└─────────────────────┘
        │
   ┌────┼───────────┐
   │    │           │
   ▼    ▼           ▼
Worker Historie   Monitoring
   │
   ▼
Datenbank

In Gegenrichtung können Commands laufen:

Backend
   │
   │ Command
   ▼
RabbitMQ
   │
   ▼
Edge / Treiber
   │
   ▼
Maschine

Damit sind Maschinenschnittstelle und Backend nicht mehr direkt voneinander abhängig. Fällt ein Backend-Worker kurzzeitig aus, produziert die Maschine trotzdem weiter. Kommt ein zweiter Datenverbraucher hinzu, muss der Maschinentreiber nicht angepasst werden. Steigt die Verarbeitungslast, können weitere Worker hinzugefügt werden.

Und fällt ein einzelner RabbitMQ-Node in einem korrekt ausgelegten Quorum-Setup aus, kann die Messaging-Ebene weiterarbeiten.

Genau dieses Prinzip nutzen wir auch bei xpand.weight

Ein konkretes Beispiel dafür ist die Integration industrieller Waagen. Bei xpand.weight kommunizieren unterschiedliche Treiber mit den jeweiligen Waagen und übersetzen deren proprietäre Protokolle in ein gemeinsames Datenmodell.

Die entstehenden Messwerte und Geräteereignisse können anschließend über RabbitMQ von der eigentlichen Verarbeitung entkoppelt werden. Befehle wie Tarieren, Nullstellen oder das Lesen eines Gewichts laufen in die Gegenrichtung über die Messaging-Schicht.

                         xpand.weight
                              │
                  ┌───────────┴───────────┐
                  │                       │
               Commands                 Events
                  │                       │
                  ▼                       ▼
               RabbitMQ               RabbitMQ
                  │                       ▲
                  ▼                       │
                Driver ───────────────► Driver
                  │
                  ▼
                 Waage

Der interessante Punkt daran ist nicht RabbitMQ selbst. Der entscheidende Punkt ist die Entkopplung.

Der Waagentreiber muss keine Datenbank kennen. Die Waage muss kein MES kennen. Das Dashboard muss das Waagenprotokoll nicht kennen. Und ein neuer Consumer kann später hinzugefügt werden, ohne den bestehenden Treiber neu zu entwickeln.

RabbitMQ ist dabei die Infrastruktur, die diese Kommunikationswege zuverlässig miteinander verbindet.

Wann ist RabbitMQ sinnvoll?

Ein Message Broker ist insbesondere interessant, wenn mehrere der folgenden Punkte zutreffen:

  1. Viele Systeme tauschen Daten miteinander aus.
  2. Producer und Consumer arbeiten mit unterschiedlichen Geschwindigkeiten.
  3. Kurzzeitige Ausfälle eines Empfängers sollen nicht sofort Datenverlust verursachen.
  4. Mehrere Anwendungen interessieren sich für dieselben Ereignisse.
  5. Verarbeitung soll horizontal skalierbar sein.
  6. Geräte und Backend sollen voneinander entkoppelt werden.
  7. Commands und Events sollen nachvollziehbar und strukturiert transportiert werden.
  8. Daten müssen über mehrere Broker-Nodes repliziert werden.
  9. Retry- und Fehlerbehandlung sollen zentral strukturiert werden.
  10. Neue Consumer sollen hinzugefügt werden können, ohne bestehende Producer zu verändern.

Wann ist RabbitMQ möglicherweise unnötig?

Nicht jede Maschine braucht einen Message Broker.

Wenn eine einzelne Anwendung einmal pro Minute einen Wert von einem einzelnen Gerät liest und diesen direkt speichert, kann eine zusätzliche Messaging-Infrastruktur unnötige Komplexität erzeugen.

Architektur sollte ein Problem lösen und nicht möglichst viele Technologien enthalten.

RabbitMQ wird interessant, wenn Verfügbarkeit, Entkopplung, Skalierung oder mehrere Kommunikationspartner tatsächlich Anforderungen des Systems sind.

Fazit: Die Queue ist nur ein Baustein

RabbitMQ kann eine sehr leistungsfähige Grundlage für industrielle Datenarchitekturen sein. Der entscheidende Vorteil liegt aber nicht darin, einfach eine Queue zwischen zwei Anwendungen zu setzen.

Die eigentliche Stärke entsteht aus der Kombination mehrerer Prinzipien:

Entkopplung
    +
Pufferung
    +
Routing
    +
Publisher Confirms
    +
Consumer Acknowledgements
    +
Quorum Queues
    +
Retries
    +
Idempotenz
    +
Monitoring

Erst daraus entsteht eine Architektur, die mit steigender Datenmenge und einzelnen Ausfällen umgehen kann.

Ein Message Broker macht ein System nicht automatisch zuverlässig. Er stellt die Mechanismen bereit, mit denen zuverlässige Systeme gebaut werden können.

Ob daraus tatsächlich eine belastbare Industriearchitektur entsteht, hängt davon ab, wie Producer, Broker und Consumer gemeinsam entworfen werden.

Und genau darin liegt der Unterschied zwischen:

„Wir haben RabbitMQ installiert."

und

„Wir haben eine skalierbare, fehlertolerante Messaging-Architektur aufgebaut."

xpand.weight

Das beschriebene Architekturprinzip in der Praxis.

RabbitMQ in der Industrie: Ausfallsichere Messaging-Architektur aufbauen – xpand