Message-Broker entkoppeln, routen und verbinden

Immer mehr Software kommuniziert mit anderer Software. Doch diese Kommunikation muss organisiert werden. HTTP(S) ist eine Möglichkeit und nutzt ein klassisches Client-Server-Modell. Mit REST ist es in den letzten Jahren sogar salonfähig geworden und bietet sich an, um Daten zwischen den vielen im Web verfügbaren Diensten auszutauschen. Ob Facebook, Twitter oder zahlreiche Content-Management-Systeme: sie bieten alle REST-Schnittstellen an und befördern das Web katapultartig voran. Jedoch sind HTTP- und REST auf Client-Server-Anwendungen beschränkt, sofern man keine weiteren Abstraktionsschichten einführt.

Die Motivation um sogenannte Message-Broker einzusetzen ist die Universalität dieser Broker. Am leichtesten zu verstehen ist ein Message-Broker, indem man sich einen in Software geschriebenen Router vorstellt, der Nachrichten empfängt und weiterverteilt. Doch dieser Router, bzw. Broker, vermittelt keine rohen Netzwerkpakete, sondern Nachrichten mit Inhalt.

Vermittlung von Nachrichten bedeutet: Auf der einen Seite sendet eine Anwendung, der sogenannte Producer, eine Nachricht an den Broker und dieser empfängt die Nachricht auf einem Eingangsport, in der Messenging-Terminologie heißt dieser Exchange. Ein Exchange hat einen bestimmten Typ, der die Weiterleitung der Nachrichten in eine oder mehrere Warteschlangen, sogenannte Queues, bestimmt. Auf der anderen Seite lauscht eine (andere) Anwendung, der sogenannte Consumer, ob Nachrichten in der Queue vorhanden sind.

Eine Queue kann mehrere Eigenschaften haben. Sie kann beispielsweise einem Consumer exklusiv zugewiesen werden, so dass nur dieser eine Consumer die Nachrichten, die in die Warteschlange einsortiert werden, zugestellt bekommt. Eine weitere Eigenschaft ist, dass eine Queue nach Beendigung der Verbindung automatisch gelöscht wird (autoDelete). Auch ist es möglich, eine Queue so zu konfigurieren, dass in der Warteschlange befindliche Nachrichten einen Neustart des Message-Brokers überleben. Eine Queue kann einen Namen haben oder anonym sein. Anonyme Queues werden beispielsweise für den Rückweg verwendet.

Das Routing findet zwischen Exchanges und Queues statt und nennt sich Binding und ist abhängig vom Typ des Exchange.

Ein Fanout-Exchange leitet eine Nachricht an alle an den Exchange gebundenen Queues weiter, ohne ein aktives Einsortieren. Das bedeutet ein Consumer legt eine (anonyme) Queue an und ebenfalls ein Binding zwischen dem Fanout-Exchange und der angelegten Queue. Wenn ein weiterer Consumer dasgleiche macht, leitet der Fanout-Exchange eine eingehende Nachricht an beide Queues weiter und somit empfangen beide Consumer die Nachricht (Publish-Subscribe-Pattern).

Direct-Exchanges sind ebenfalls mit einer oder mehreren Queues verbunden. Jedoch enthält ein Binding einen sogenannten Routing-Key. Kommt nun eine Nachricht an einem Direct-Exchange an, wird der Routing-Key in der Nachricht untersucht und die Nachricht nur an diejenigen Queues weitergeleitet, bei denen ein Binding mit dem entsprechenden Routing-Key besteht (Routing-Pattern). Beispielsweise gibt es eine Queue, die mit dem Routing-Key “wichtig” gebindet wurde, und eine Queue namens “Spam”, die mit dem Routing-Key “unwichtig” gebindet wurde. Kommt eine wichtige Nachricht an, wird sie in die wichtige Queue einsortigt und unwichtige Nachrichten landen in der Spam-Queue. Es ist möglich, dass mehrere Queues mit dem gleichen Routing-Key mit einem Direct-Exchange verbunden sind. Beispielsweise werden zwei Bindings mit dem Routing-Key “möglicherweise wichtig” vom Direct-Exchange erstellt, die zu beiden Queues führen. Eine möglicherweise wichtige Nachricht landet dann in beiden Queues.

Der dritte Typ heißt Topic-Exchange und setzt das Topic-Pattern um. Der Topic-Exchange wird wiederum mittels Bindings mit Queues verbunden. Doch der Routing-Key der Bindings enthält nun eine Liste von Wörtern, die mit einem Punkt getrennt werden. Diese Liste von Wörtern ist hierarchisch aufgebaut. Beispiel: Queue 1 ist per Binding mit dem Routing-Key “schaeffernet.andreas.raspberry” und Queue 2 mit dem Routing-Key “schaeffernet.*.laptop” verbunden. Die Verarbeitung der Topics ähnelt etwas einer sehr einfachen Regular Expression. So müssen die Topics im Routing-Key der eingehenden Nachricht mit den Topics im Routing-Key des Bindings übereinstimmen. Wie zu sehen ist, sind Platzhalter erlaubt. Während das Binding zu Queue 1 exakt stimmen muss, erlaubt das Binding zu Queue 2 alle Vornamen im zweiten Topic. Interpretieren könnte man den ersten Routing-Key mit “ich möchte die Nachricht an den Raspberry Pi von Andreas Schaeffer zustellen”. Der zweite Routing-Key würde “ich möchte die Nachricht an alle Laptops der Familie Schaeffer zustellen” bedeuten.

Mit diesem relativ einfachen, aber flexiblen System lassen sich nun verschiedenste Anwendungsfälle umsetzen:

1. Ein Producer sendet an eine benamte, exklusive Queue. Die Nachrichten werden von exakt einem Consumer empfangen.

2. Ein Producer sendet an eine benamte, nicht-exklusive Queue. Die Nachrichten werden von einem oder mehreren Consumer empfangen (Worker-Pattern).

3. Ein Producer sendet an einen Fanout-Exchange. Mehrere Consumer legen anonyme Queues an, die sie mit dem Exchange verbinden (Publish-Subscribe-Pattern).

4. Ein Producer sendet an einen Direct-Exchange. Die Nachricht enthält einen Routing-Key. Mehrere Consumer legen anonyme Queues an, die sie mit dem Exchange unter Nutzung eines Routing-Keys verbinden (Routing-Pattern). Eine Queue kann mit mehreren Bindings und unterschiedlichen Routing-Keys verbunden werden (Multiple-Bindings).

5. Ein Producer sendet an einen Topic-Exchange. Die Nachricht enthält einen Routing-Key, der die Topics-Expression beinhaltet. Mehrere Consumer legen anonyme Queues an, die sie mit dem Exchange unter Nutzung eines Routing-Keys, die die Topics-Expression beinhalten, verbinden (Topic-Pattern). Eine Queue kann mit mehreren Bindings und unterschiedlichen Topic-Expressions verbunden werden (Multiple-Bindings).

6. Ein Producer sendet an eine benamte, exklusive Queue. Die Nachricht enthält eine Reply-To-Adresse sowie eine Id, von wem die Nachricht stammt. Exakt ein Consumer empfängt die Nachricht, führt eine Remote-Procedure aus und sendet eine Nachricht mit dem Ergebnis der Ausführung an eine zweite, anonyme, exklusive Queue (RPC-Pattern).

Diese Anwendungsfälle zeigen bereits, wie flexibel ein Message-Broker eingesetzt werden kann. Da wir nun wissen wie ein Message-Broker funktioniert, wird an dieser Stelle noch ausgeführt, welche weiteren Vorteile der Einsatz eines Message-Brokers zu erwarten sind.

Zum Ersten erfolgt die Kommunikation über ein standardisiertes Protokoll namens AMQP. Dieses Protokoll wird von einer ganzen Reihe von Message-Broker-Implementierungen wie beispielsweise RabbitMQ oder ZeroMQ unterstützt. Damit ist man nicht abhängig von einem bestimmten Message-Broker. Die meisten Message-Broker sind darüber hinaus Open-Source und plattformunabhängig.

Weiterhin gibt es für zahlreiche Programmiersprachen Client-Bibliotheken. Das bedeutet, dass die Anwendungen, die miteinander kommunizieren, nicht zwingend in der gleichen Programmiersprache geschrieben sein müssen. Dies ist ein Vorteil gegenüber RMI oder JMS. Der Transportweg kann zudem mit OpenSSL verschlüsselt werden.

Message-Broker sind für einen hohen Durchsatz ausgelegt. Es gibt Anwendungsfälle, in den mehrere Millionen Nachrichten pro Tag verarbeitet werden. Zudem erlauben manche Message-Broker wie beispielsweise RabbitMQ das Clustering von Message-Brokern.

Der größte Vorteil ist jedoch die Entkoppelung. Software kann in kleinere Einheiten aufgespalten werden, die möglicherweise auf unterschiedlichen Rechnern ausgeführt werden und nur an den notwendigen Stellen miteinander kommunizieren. Dabei ist eine heterogene Landschaft kein Problem: in C# programmierte Desktop-Clients auf Windows können mit in Java programmierten Management-Anwendungen auf leistungsstarken Linux-Servern kommunizieren. Oder eine Farm von tausenden von Rechenknechten organisieren dezentral untereinander, welche Aufgaben welche Instanz als nächstes berechnen soll (P2PoB). Oder die Zentralisierung von Logging. Oder die Automatisierung von Rechenzentren…

Comments are closed