Messaging
Die Verwendung synchroner Aufrufe - insbesondere in der Hintergrundverarbeitung - bringt, wie in Kommunikationsarten beschreiben, Probleme mit sich. Insbesondere Zustellsicherheit, schlechtere Skalierbarkeit und höhere Kopplung sind kritisch.
Als Ausweg wird hier die asynchrone Kommunikation verwendet. Doch wie wird diese in einem verteilten System implementiert?
Als Lösung verwendet man im Allgemeinen einen Message Broker. In Schleupen.CS verwenden wir RabbitMQ. Da über diesen Nachrichten (= Messages) ausgetauscht werden, spricht man auch vom Messaging. Messaging ist also ein Kommunikationsmuster zwischen Software-Bausteinen, das in verteilten Systemen genutzt wird, um
- Sender und Empfänger zeitlich voneinander zu entkoppeln
- Zustellsicherheit zu gewährleisten
- die generelle Skalierbarkeit zu erhöhen
- die Erweiterbarkeit des Systems zu vereinfachen
- einzelne Services technologisch zu entkoppeln.
Hierbei werden in Schleupen.CS verschiedene Arten der Kommunikation verwendet, die im Folgenden erläutert werden.
Aktuell verwendet die Schleupen.CS Plattfom den Message Broker RabbitMQ, der die unterschiedlichen Kommunikationsformen wie Command Messages, Publish/Subscribe und Request/Reply unterstützt. Die Architektur der Plattform abstrahiert aber über den Message Bus vom konkrekt verwendeten Message Broker, um diesen - falls notwendig - möglichst ohne Rückwirkung auf die Domänen und Geschäftsprozesse ersetzen zu können.
Zum besseren Verständnis, erläutern wir dennoch die Abläufe "hinter den Kulissen" am Beispiel von RabbitMQ.
Grundlegendes
Zentral bei Messaging ist die Verwendung von Queues. Dabei handelt es sich im Kern im eine sogenannte FIFO-Queue (first-in, first-out). Eine derartige Queue ist in der Regel persistent um Zustellsicherheit zu gewährleisten. Diese Queue wird gefüllt durch den sogenannte Producer, der Nachrichten in die Queue einstellt.
Empfänger der Nachrichten ist im Allgemeinen der Consumer. Dabei gewährt der Message Broker At-Least-Once-Zustellung, d.h. es kann sein, dass der Conumer eine Nachricht mehrfach erhalten kann und auch hier Idempotenz relevant ist.
Event-Driven Architecture - Business Events
Publish/Subscribe ist ein asynchrones Kommunikationsmuster, bei dem der Producer Nachrichten an ihm unbekannte Consumer sendet. Diese Consumer haben sich zuvor "subscribed", also den Empfang bestimmter Nachrichten abonniert. In diesem Pattern nennt man den Producer Publisher und den Consumer Subscriber. Das folgende Diagramm zeigt den generellen Ablauf. Wichtig dabei ist, dass der Subscribe-Vorgang nur einmal erfolgen muss und der Subscriber ab diesem Zeitpunkt alle Nachrichten erhält.
Im Message Broker RabbitMQ bildet sich dies wie folgt in der RabbitMQ-Terminologie ab:
Schaut man sich die Topologie von Schleupen.CS im RabbitMQ genau an, so kommen mehrere Exchanges zur Anwendung. Die Topologie weicht entsprechend von den Darstellungen hier ab. Das ist aber für das Grundverständnis nicht relevant.
Das Nachrichten-Format wird wie oben bereits gezeigt, durch eine WSDL beschrieben. Wichtig hierbei ist, dass diese Schnittstellenbeschreibung im Gegensatz zu "normalen" Services dem Publisher gehört. Daher ist auch das Exchange in der Abbildung blau eingefärbt - es gehört dem Publisher. Entsprechend gehört die Queue dem Subscriber.
In Schleupen.CS 3.0 ist ein Publisher und Subscriber entweder eine GP-Komponenten oder ein Land. Das Auslösen eines Events wird wie in Auslösen eines Business Events beschrieben implementiert. Der Subscriber wird wie in Service-Fassade beschrieben umgesetzt.
Die Event-Driven Architecture von Schleupen.CS nutzt das Publish/Subscribe Muster zur Realisierung von Geschäftsereignissen (Business Events). Das Auslösen von fachlichen Ereignissen findet an sehr vielen Stellen statt und ist demzufolge ein wichtiges architektonisches Ausdrucksmittel.
Command Messages
Häufig ist es sinnvoll, einem bestimmten Consumer Nachrichten zu senden. Hierzu sendet der Producer über ein dem Consumer gehörendes Exchange und einer gebundenen Queue dem Consumer eine Nachricht wie in folgender Abbildung dargestellt. Die Kommunikation ist gerichtet und der Producer weiß, wem er die Nachricht sendet. Die Topologie zeigt folgende Abbildung.
Hier gehört die Schnittstellenbeschreibung dem anbietenden "Service", also dem Consumer. Zur Verdeutlichung sind Exchange und Queue rot gefärbt.
In Schleupen.CS 3.0 ist ein Producer entweder eine GP-Komponente oder ein Land - Consumer sind im Standard in Ländern implementiert. Das Auslösen eines Events wird, wie in Aufruf eines asynchronen Service beschrieben, implementiert. Der Consumer wird, wie in Service-Fassade beschrieben, umgesetzt.
Request/Reply
Bei synchronen Service erhält der Aufrufer direkt, d.h. im selben Thread eine Antwort. Doch wie implementiert man dies beim Messaging?
Request/Reply hat als Ziel, eine Nachricht an einen bestimmten Consumer zu senden und von diesem eine Antwort zu erhalten. In diesem Muster heißt der Producer Requestor und der Consumer Replier. Das Muster kann als zwei gegenläufige synchrone Aufrufe verstanden werden. Der Aufruf des Requestors kehrt sofort zurück (ohne die angeforderte Antwort). Während des Wartens auf die Antwort des Repliers, welche durch den Aufruf der vom Requestor zur Verfügung gestellte Callback-Schnittstelle erfolgt, ist der Requestor nicht blockiert. Die Gesamtverarbeitung ist also asynchron.
Hier gehört die Schnittstellenbeschreibung dem anbietenden "Service".
Im Vergleich zu einem synchronen Service-Aufruf gibt es einen wichtigen Unterschied: Der Client-Prozess wird nicht blockiert, sondern in einem losgelösten Kontext ausgeführt. Das heißt beispielsweise, dass in Schleupen.CS der Zustand persistiert und dann neugeladen werden muss, um die Nachricht zu verarbeiten.
In Schleupen.CS 3.0 ist ein Requestor in einer GP-Komponente und ein Replier im Standard in Ländern definiert. Der Aufruf eines Service per Request/Reply ist in Workflows beschrieben
Fehlerbehandlung
Die Fehlerbehandlung im Messaging ist anders als bei synchronen Serviceaufrufen, da dem Aufrufer nicht unmittelbar der Fehler mitgeteilt werden kann. Im Allgemeinen landen nicht erfolgreich verarbeitete Nachrichten im DeadletterMessageStore. Aus diesem kann durch den Benutzer oder automatisiert die Nachricht erneut in Verarbeitung gesendet oder auch verworfen werden. Für Request-Reply besitzt Schleupen.CS 3.0 spezielle Reply-Schnittstellen, die Callback-Schnittstellen sind. Diese muss der Requestor bereitstellen. Der Replier ruft diese im Erfolgs- und im Fehlerfall auf.