© Arak Rattanawijittakorn/Shutterstock.com
Asynchrone Prozesse in Symfony realisieren

Die neue Messenger-Komponente


Eins der großen, neuen Features in Symfony 4 ist die im letzten Release veröffentlichte Messenger-Komponente. Vergleichbar mit dem EventDispatcher versendet sie Nachrichten, kann diese aber auch über Anwendungsgrenzen hinweg senden und empfangen. Damit bietet sie das Potenzial, Event Sourcing und CQRS in Symfony-Anwendungen einzuführen.

Die Messenger-Komponente implementiert das Message-Bus-Entwurfsmuster [1]. Im Kern geht es dabei um das Senden und Empfangen von Nachrichten. Auf diese Weise kann eine asynchrone Kommunikation zwischen Anwendungen ermöglicht werden. Eine Nachricht ist vergleichbar mit den bereits bekannten Events aus Symfony. Sie kapselt Daten, die von einer Absenderklasse an eine Empfängerklasse übermittelt werden. Welche Nachricht an welchen Empfänger übermittelt wird, wird vom Messagebus bestimmt. Das MessageBusInterface, über das man Nachrichten verschickt, hat nur eine Methode namens dispatch, der man ein beliebiges PHP-Objekt als Nachricht übergeben kann. Standardmäßig wird das Objekt mit der Serializer-Komponente in einen JSON-String umgewandelt und an einen Transport weitergereicht. Bei asynchroner Verarbeitung wird die Nachricht über einen Transport weitergeschickt. Ebenso holt der Messenger Nachrichten aus dem Transport, um sie zu verarbeiten. Aktuell unterstützt die Messenger-Komponente zwei native Transporte. Standardmäßig werden Nachrichten ohne Umwege, in der gleichen Anwendung, im selben PHP-Prozess synchron verarbeitet. Das ist vor allem für das Debugging hilfreich, da man zum Beispiel mit XDebug einfach nachverfolgen kann, wie die Nachricht verarbeitet wird. Der zweite Transport verwendet das AMQP-Protokoll über die entsprechende PHP-Extension zum Versand der Nachricht an eine Messagequeue und zum Empfangen von Nachrichten aus einer Messagequeue. Welcher Transport für welche Nachricht verwendet werden soll, wird über das Routing in der Konfiguration des Busses gesteuert. Um Nachrichten aus einer Messagequeue zu verarbeiten, kann ein CLI-Befehl ausgeführt werden. Neben den beiden unterstützten können über Adapter auch bereits vorhandene Transports zum Beispiel aus der beliebten Enqueue-Bibliothek [2] weiterverwendet werden. Das ist auch sinnvoll, wenn die AMQP-Extension nicht zur Verfügung steht und stattdessen eine PHP-Implementierung verwendet werden soll.

Für die Verarbeitung empfangener Nachrichten muss eine Klasse angelegt werden, die die magische Methode __invoke implementiert, deren einziges Argument das übermittelte PHP-Objekt, also die Klasse der ursprünglich versendeten Message ist. Diese Klasse muss dann im Messagebus als Handler registriert werden. Das passiert in einer Symfony-Anwendung über einen Service-Tag messenger.message_handler. Alternativ kann ein leeres Handler Interface implementiert werden, damit Symfonys Dependency-Injection-Komponente per Autokonfiguration den Tag automatisch hinzufügen kann.

Um die Messenger-Komponente in einer Symfony-4-Anwendung nutzen zu können, muss die Komponente zuerst mit Composer installiert werden. Ein Flex Recipe legt dabei automatisch eine Basiskonfiguration an und stellt darüber einen Messagebus zur Verfügung, der ohne weitere Konfiguration verwendet werden kann. Auskommentierte Teile der Konfiguration zeigen außerdem, wie statt der lokalen, synchronen Verarbeitung der asynchrone AMQP-Transport konfiguriert werden kann. Die Konfiguration aus dem Recipe kann auch in einer bestehenden Anwendung ohne Flex übernommen werden. Neben dem standardmäßig bereitgestellten Bus lassen sich auch eigene Busse definieren. Das ist zum Beispiel dann sinnvoll, wenn diese sich unterschiedlich verhalten sollen. Das erlaubt es, verschiedene Typen von Bussen zu definieren, zum Beispiel einen Command-Bus, der sicherstellt, dass eine Nachricht von exakt einem Handler verarbeitet wird. Aber auch einen Eventbus, der erlaubt, dass kein Handler für die Nachricht registriert ist. Standardmäßig muss mindestens ein Handler registriert sein und Nachrichten werden an alle registrierten Handler weitergereicht.

Middlewares

Middlewares dienen dazu, das Verhalten eines Messagebusses beim Empfangen, Versenden oder Verarbeiten von Nachrichten zu steuern. Serienmäßig registriert die Messenger-Komponente auf allen Bussen, die man definiert, drei Middlewares:

  • LoggingMiddleware: protokolliert die Verarbeitung von Nachrichten im Log

  • SendMessageMiddleware: sendet Nachrichten an Transports bei asynchroner Verarbeitung

  • HandleMessageMiddleware: ruft registrierte Handler für die Nachricht auf

Diese lassen sich per Konfiguration über den Flag default_middleware deaktivieren. Außerdem lassen sich weitere Middlewares hinzufügen. Aktuell stehen zwei weitere Middlewares zur Verfügung. Die ValidationMiddleware stellt sicher, dass die Message gültige Daten enthält. Die AllowNoHandlerMiddleware verhindert die Exception, wenn kein gültiger Handler für eine Nachricht registriert wurde.

Schreibt man eigene Middlewares, muss das entsprechende Interface implementiert werden. Darüber hinaus sollte man darauf achten, dass die Middlewares sequenziell abgearbeitet werden. Insofern ist die Reihenfolge, in der sie registriert werden, relevant und nur schwer kontro...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang