[Software-Architektur] Das Pipeline-Entwurfsmuster – von Null bis zum Helden

In diesem Artikel werden wir die verschiedenen Möglichkeiten zur Implementierung des Pipeline-Entwurfsmusters untersuchen, angefangen bei den Grundlagen bis hin zu komplexeren Lösungen.

Das Pipeline-Muster

Das Pipeline-Muster ist ein Software-Entwurfsmuster, das die Möglichkeit bietet, eine Abfolge von Operationen aufzubauen und auszuführen.

https://www.hojjatk.com/2012/11/chain-of-responsibility-pipeline-design.html

Dieses Muster wird besser in Verbindung mit dem Plugin-Muster verwendet, um die Pipeline beim Start der Anwendung dynamisch aufzubauen.

Sequenz

Die einfachste Implementierung einer Pipeline wäre eine einfache Sequenz von Operationen.

Die Schnittstelle einer Operation kann aufgerufen werden, um Daten zu verarbeiten.

Die Pipeline verarbeitet jede Operation nacheinander. Die Pipeline-Klasse implementiert auch die IOperation-Schnittstelle, so dass sie kombiniert werden können.

Die Operation kann in einer dedizierten Klasse geschrieben werden.

oder einen Wrapper verwenden, um automatisch eine Operation aus einem Lambda zu erstellen.

Die Pipeline-Operationen sollten registriert werden, bevor die Pipeline aufgerufen wird.

Schaltkreisunterbrecher

Die erste Funktion, die Sie zu Ihrer Pipeline hinzufügen möchten, ist ein Schaltkreisunterbrecher.

Jede Operation gibt das Ergebnis zurück: fehlgeschlagen oder erfolgreich.

Wenn eine Operation fehlschlägt, sollte die Ausführung der Pipeline gestoppt werden.

Asynchron

Eine weitere Anforderung könnte sein, eine Pipeline zu haben, die mit asynchronen Operationen umgehen kann.

Jede Operation muss nun die nächste Operation in der Pipeline aufrufen, nachdem sie mit der Verarbeitung der Daten fertig ist.

Die Pipeline ist ein wenig komplexer, da sie die nächste Operation festlegen muss, wenn eine neue Operation registriert wird. Eine andere Lösung ist die Verwendung eines Builders.

Diese Operation ist asynchron und wird in einem eigenen Thread ausgeführt, wenn die Zeit abgelaufen ist, wird die nächste Operation aufgerufen, um die Pipeline fortzusetzen.

Die generische Operation kann sowohl mit einer einfachen Aktion verwendet werden, als auch mit einem eingebauten Unterbrecher, wenn Sie eine Funktion verwenden.

Dieses einfache Beispiel verwendet mehrere der von uns implementierten Funktionen.

Jetzt wissen Sie, wie Sie Ihre Pipeline asynchron machen können!

Es wäre noch besser, wenn man einen weiteren Callback zur vorherigen Operation hätte, damit das Ergebnis im Gegenstrom durch die Pipeline läuft.

Plugin

Der Hauptgrund für die Verwendung des Pipeline-Design-Patterns ist oft die Anforderung, Plugins hinzufügen zu können, die entweder Operationen an eine bestehende Pipeline anhängen oder eine Operation in der Mitte der Pipeline einhaken.

Die Pipeline ist wirklich einfach, aber dieses Mal sind die Operationen offengelegt.

Lassen Sie uns eine einfache Anwendung mit einer Pipeline nehmen, die nur die 3 Schritte in der Konsole anzeigt. Diese Anwendung unterstützt auch Plugins, um die Pipeline zu modifizieren.

Das erste Plugin wird eine weitere Operation in den zweiten Slot der Pipeline einhängen.

Das zweite Plugin wird eine neue Operation am Ende der Pipeline anhängen.

Die Anwendung und die Plugins sind zusammengefügt, wir können die Pipeline aufrufen.

Batch

Eine weitere nützliche Funktion ist es, Stapeldaten in der gleichen Pipeline verarbeiten zu können als einzelne Elemente.

Die Batch-Pipeline umhüllt eine Pipeline und ruft jede Operation für jedes Element auf.

Jedes Element wird umhüllt, so dass wir uns das Ergebnis des Unterbrechers merken können.

Diese Operation prüft, ob die Ganzzahl die erforderliche Größenordnung hat.

Die Pipeline wird die Größenordnung eines Stapels von Ganzzahlen prüfen.

Die Pipeline ruft die nächste Operation nur für Elemente auf, die nicht fehlgeschlagen sind.

High Performance Pipeline

Das Pipeline Design Pattern kann sich auch auf eine sehr viel spezifischere und leistungsorientierte Softwarearchitektur beziehen.

Einige Projekte verwenden eine Pipeline, um die Verarbeitung einer großen Datenmenge zu optimieren, indem jede Operation der Pipeline in einem eigenen Thread ausgeführt wird.

Jeder Thread konsumiert und produziert Daten aus einer gleichzeitigen Warteschlange, die als Puffer dient.

Diesmal werden wir asynchrone Operationen mit einem Circuit-Breaker verwenden.

Wenn die Operation erfolgreich ist, sollte sie als nächstes aufgerufen oder bei Fehlschlag beendet werden.

Die Pipeline ist so konzipiert, dass sie den circuit-breaker umgeht. Erfolg oder Misserfolg, sie wird immer die übergeordnete Pipeline-Sequenz fortsetzen und die nächste Operation aufrufen.

Das Szenario ist etwas komplex, daher werde ich nicht alles erklären. Die Idee ist, verschiedene Threads zu haben, um eingehende Aufträge zu verarbeiten. Wenn eine Bestellung fertig bearbeitet ist, überprüfen wir den Status der Bestellung.

Jeder Bestellungsprozessor ist in einem eigenen Thread isoliert, so dass Sie optimieren können, wie Sie die Daten speichern und direkten Speicherzugriff haben, ohne Sperren zu verwenden.

Der Zahlungsbestellungsprozessor ist der einzige Thread, der auf Benutzersalden zugreifen kann. Er kann jeden Saldo abrufen oder aktualisieren, ohne sich um Gleichzeitigkeitsprobleme zu kümmern.

Die Pipeline verarbeitet die Abfolge von Operationen so schnell wie möglich.

Fazit

Das Pipeline Design Pattern kann auf sehr unterschiedliche Art und Weise implementiert werden, von einer einfachen Befehlskette bis hin zu einem komplexeren Workflow.

Viele Funktionen können erforderlich sein, wie z.B. Circuit-Breaker, asynchron oder Puffer, aber meistens möchte man Pipelines verwenden, wenn man Plugins haben möchte, die sich in den Pipeline-Ausführungsfluss einklinken können.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.