Kommuikationsarten:
Synchrone und asynchrone Kommunikation

Die Schleupen.CS Plattform unterstützt verschiedene Arten der Kommunikation zwischen den Bausteinen auf der Ebene der Makroarchitektur. Hierbei unterscheiden wir zwei querschnittliche Aspekte:

Der erste Aspekt beachtet, ob die Kommunikation ein synchrones oder asynchrones Protokoll verwendet:

  • Bei synchroner Kommunikation nutzt der Client über HTTP Services, wobei der Client einen Request absetzt und die aktuelle Verarbeitung solange blockiert wird, bis eine Antwort zurückgegeben wird. Dabei ist es nicht entscheidend, ob die lokale Code-Ausführung des Clients den lokalen Thread blockiert oder nicht, sondern dass der Client-Code seine Arbeit erst dann fortführen kann, wenn eine Antwort seitens des HTTP Servers gegeben worden ist.
  • Anders verhält es sich bei asynchroner Kommunikation via Messaging mithilfe des asynchronen Protokolls AMQP, bei der der Client nicht blockiert wird. Hierbei wird eine Message abgegeben und nicht auf die Antwort gewartet, also nicht blockiert. Erwartet der Client eine Antwort, so muss die Arbeit zwischenzeitlich zur Seite gelegt werden (i.A. persistent), da dieser sonst zustandsbehaftet wäre.

Der zweite Aspket der Kommunikation zwischen Bausteinen bezieht sich darauf, ob es einen oder mehrere Empfänger gibt:

  • Bei einem Empfänger gibt es genau einen Adressaten der die Nachricht erhält. Diese Kommunikation kann sowohl synchron als auch asynchron erfolgen.
  • Gibt es mehrere Empfänger, so muss die Kommunikation asynchron erfolgen. Ein Beispiel hierfür ist das Pattern Publish / Subscribe, bei dem ein Event ausgelöst wird und an die Konsumenten gesendet wird, die dieses Event abonniert haben.
Asynchrone Kommunikation fördert die Autonomie der Bausteine

Sowohl die synchrone als auch die asynchrone Kommunikation haben ihre Berechtigung. Synchrone Kommunikation ist insbesondere einfacher zu implementieren - insbesondere aus Benutzeroberflächen heraus.

Demgegenüber steht, dass bei synchroner Kommunikation u.a. folgende Punkte kritisch sind:

  • Skaliert weniger gut
    • Die während des Aufrufs blockierten Ressourcen des Clients, können während des Aufrufs nicht genutzt werden. Es findet keine weitere Verarbeitung auf Seiten des Aufrufers im gleichen Thread statt, bis der Aufgerufene seine Arbeit erledigt hat und der Aufruf beendet ist (async / await schwächt dieses ab, erhöht aber lediglich den Durchsatz).
    • Verfügt die Client-Seite über ausreichende Ressourcen und soll sie während des Aufrufs andere Aufgaben erledigen, muss extra Aufwand getrieben werden, um die nebenläufige Verarbeitungen zu ermöglichen. Dies kann z.B. in Frontends gewünscht sein, um den Anwender durch die Blockade nicht einzuschränken und das UI responsiv zu gestalten.
    • Das Abbrechen synchroner Aufrufe ist durch das blockierende Verhalten nicht einfach möglich. Wird nur das Warten des Clients auf die Antwort abgebrochen, läuft die Verarbeitung auf der Anbieterseite weiter.
  • Risiko von Timeouts, Client von Server entkoppelt
    • Da ein Client stets Timeouts nutzen sollte, da Ressourcen ggf. unendlich lang blockiert bleiben, kann dies dazu führen, dass die Verarbeitung Server-seitig fortgeführt und Client-seitig abgebrochen wird. Hieraus ergibt sich beispielsweise aus Client-Sicht die Frage, was und ob Server-seitig etwas passiert. Dies löst man im Allgemeinen durch Retries, was wiederum zu höherer Last etc. führt. Dabei müssen dann Service idempotent implementiert werden.
    • Werden mehrere synchrone Services hintereinander in einer Aufrufkette verwendet, nimmt die Empfindlichkeit für Timeouts weiter zu. Durch die in der Kette entstehende Kopplung ist es aufwendiger, ein resilientes Verhalten zu implementieren.

Asynchrone Kommunikation hingegen hat nicht die Nachteile bzgl. der Skalierbarkeit, da parallele Verarbeitung einfacher möglich wird. Auch sind etwaige Probleme bzgl. Timeouts unkritischer und weniger immanent vorhanden, da Timeouts keine Client-seitiges Problem mehr sind.

Diese Form der Kommunikation fördert insbesondere die Autonomie der Bausteine zur Laufzeit. So kann die Nachricht an den Consumer zu beliebigen Zeitpunkten zugestellt werden - es wird berücksichtigt ob der Ziel-Baustein verfügbar ist und wie die aktuelle Last auf den Servern ist. Dies macht die Lösung in Summe resilienter. Dies funktioniert, da die Message Consumer selbstständig Nachrichten aus dem MessageBus lesen und die Aktionsrichtung umgekehrt ist (pull anstatt push).

Die Implementierung ist im allgemeiner komplexer weil nebenläufiger. Wird beispielsweise eine Antwort erwartet, erfordert dies in der Regel mehr Code für die Benachrichtigung des Aufrufers darüber, dass die Verarbeitung abgeschlossen wurde. Um die ursprüngliche Arbeit dann fortzuführen, muss im Allgemeinen die Arbeit bei Abgabe des Requests persistiert werden, um nach Erhalt der Antwort diese wieder fortführen zu können. Auch die Behandlung von Fehlern wird entsprechend deutlich schwieriger.

Siehe auch: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/architect-microservice-container-applications/communication-in-microservice-architecture

Cookie Consent mit Real Cookie Banner