Systemübersicht und Serverdienste
Um einen besseren Einstieg in das System zu bieten ist es hilfreich, zunächst zu verstehen welche Dienste dem System zur Verfügung stehen. Dies gibt ebenfalls einen besseren Überblick darüber, welche Möglichkeiten der Anwendungsebene überhaupt geboten werden.
Dienst | Verwendung in synQup |
---|---|
RabbitMQ | Message Queue Server verteilt Modul- und Core-Aufgaben an Arbeitsprozesse (Worker) |
MongoDB | schemalose NoSQL-Datenbank hält neben den sogenannten Transferdaten (z.B.: Produkte, Bestellungen…) in einer getrennten Datenbank auch Nutzdaten des Systems (Logs, Zeitseriendaten) |
MySQL | RDBMS hält Systemkonfiguration (Nutzer, Flows, Ausführungen…) |
Redis | RAM-gestützter Key-Value-Storage bietet Modulen und Core unkompliziertes Caching, Möglichkeit zur Threadsynchronisation u.V.m. |
synQup selbst besteht aus Modulen und Core (die Trennung wird im nachfolgenden Abschnitt genauer beschrieben) und stellt eine PHP-Anwendung auf Symfony-Basis dar.
Module & Core
Datenverarbeitung innerhalb von synQup besteht immer aus einem Zusammenspiel der Hauptanwendung (des sogenannten Cores) und mindestens einem Modul. Die Aufgaben von Core und Modul werden im Folgenden exemplarisch als Abgrenzung dargestellt:
Aufgaben Core:
- Prozessmodellierung (Welche Module spielen wie zusammen?)
- Ablaufsteuerung (Starten der Module)
- Monitoring (Überwachung des Modulfortschritts)
- Alerting
- Dateisystemabstraktion
- Authentifizierung & Authorisierung (Welcher Nutzer darf was und wie ist dessen Identität zu bestätigen?)
- Zentralisiertes Logging
- Bereitstellung der Oberfläche
Im Kontrast hierzu übernehmen die Module spezifische Integrationsaufgaben. Das kann beispielsweise das Anbinden eines bestimmten ERP-Systems oder das Einlesen eines bestimmten Datenformates sein. Wirklich mächtig sind Module erst dann, wenn sie wiederverwendbar und erweiterbar gestaltet werden.
Flows & Executions
Ein Flow ist eine abstrakte Datenflussbeschreibung aller Teilschritte, die Daten durchlaufen müssen. Die Instanz eines Flows, also eine konkrete Ausführung, nennt man Execution bzw. FlowExecution. Ein einfaches Beispiel für einen Flow könnte so aussehen:
Flow A:
- Lade die Datei
~/DATEN.zip
vom FTP-Server ftp.example.com mit den Benutzerdatenexample
/password
in das lokale DateisystemdemoFs
herunter. (I) - Entpacke die Zip-Datei
DATEN.zip
im Root-Ordner des lokalen DateisystemsdemoFs
. (I) - Lese die CSV-Datei
daten.csv
imdemoFs
Dateisystem mit der Spaltenbeschreibung(…)
ein. (I) - … (T)
- Exportiere alle Produkte, die sich seit dem letzten Update geändert haben in den Shopware 6 Webshop
shop.example.com
mit den API-Zugangsdaten(…)
(O)
Gehen wir davon aus, dass dieser Flow mit einem Cronjob gesteuert wird und jeden Tag um 10:00 gestartet wird. Eine FlowExecution ist also zum Beispiel die konkrete Ausführung des Flow A am 16.09.2021 um 10:00.
Schön zu erkennen ist an diesem Flow-Beispiel auch die Trennung von Konfiguration und Modulcode:
Das FTP-Modul wird hier für unseren Beispielkunden für den FTP-Server ftp.example.com
konfiguriert. Beim nächsten Kunden könnte dieser Host example.org
lauten.
In einem Flow werden also konfigurierbare, wiederverwendbare Module in ein Zusammenspiel gebracht.
Auch wird hier deutlich, dass synQup auch die Abstraktion der Dateisystem-Ebene übernimmt.
Das verwendete Storage-Backend ist also in Richtung der Module eine Black-Box. Hierdurch lassen sich auch komplexe Anforderungen
realisieren und die Integration in bestehende Systemlandschaften wird vereinfacht: hinter demoFs
im obigen Beispiel könnte sich
also ein lokales Dateisystem, ein (S)FTP-Server oder auch ein AWS S3 Bucket verbergen.
Alle Schritte in einem Flow lassen sich in einen von drei Namespaces einsortieren: Eingabe, Verarbeitung und Ausgabe.
Im Core finden sich die englischsprachigen Bezeichnungen input, transformations und output wieder.
Diese Schritte laufen sequentiell und in sich geschlossen ab: die Schritte im Namespace output
können erst laufen, nachdem alle transformations
-Schritte fertig gelaufen sind.
Zur einfacheren Organisation zeigt es sich als best practice, auch bei eigenen Modulen immer aus der Perspektive der Transferdatenbank eine Entscheidung zu treffen, in welchen Namespace ein (Teil-)modul einsortiert wird: ein Modul, dass Daten aus der Transferdatenbank in ein Zielsystem exportiert wäre also ein Ausgabemodul. Ein Modul, welches Daten in die Transferdatenbank hinzufügt, wäre ein Eingabemodul, ein Modul welches auf Daten in der Transferdatenbank arbeitet ein Verarbeitungsmodul.
Ablaufsteuerung
Ein Flow besteht also immer aus verschiedenen Schritten: bestimmte Module (in den obengenannten Gruppen), die nacheinander laufen. Dieses "nacheinander" ist bei synQup flexibel zu betrachten: neben den oben genannten Prozesschritten Eingabe, Verarbeitung und Ausgabe lässt sich auch der Ablauf innerhalb eines einzelnen Prozessschritts (also beispielsweise das Zusammenspiel aller Input-Module für einen bestimmten Flow) konfigurieren: hierfür werden sogenannte DispatchConditions eingesetzt (also Bedingungen, die erfüllt werden müssen, bevor ein Modul gestartet werden kann).
In unserem obigen Beispiel finden sich bereits implizierte Dispatch-Conditions wieder: die Module im Input-Schritt (blau markiert) würden, ohne DispatchConditions zunächst alle gleichzeitig mit dem Beginn des Input-Schritts gestartet werden (Nebenläufigkeit innerhalb der Prozessschritte, sequentieller Ablauf zwischen den Prozessschritten). Wie müsste also eine solche DispatchCondition aussehen? Auf was würde sich diese überhaupt beziehen?
Progress-Handling
Alle startbaren (Teil-)module in synQup müssen eine sogenannte ProgressDefinition bereitstellen: diese ProgressDefinition ist eine Blaupause der vorgesehenen Abarbeitung einer bestimmten Aufgabe. Ein einfaches (in der Praxis sind diese ProgressDefinitions u.U. komplexe Baumstrukturen) Beispiel für einen CSV-Import:
- Modulstart: Prüfen des Vorhandenseins der zu importierenden Datei, Formatprüfung
- Aufteilen der Datei in Batches
- Verarbeitung dieser Batches hin zu Transferprodukten
- Aufräumen: Löschen/Verschieben der nun importierten Datei
Aus dieser Blaupause wird jedes Mal, wenn ein Flow startet, ein Eintrag in einer Tabelle erzeugt. Das jeweilige Modul hat die Verantwortung darüber, dem Core beim Start der Verarbeitung die Zahl der gesamten Prozesschritte (total count) und in der Verarbeitung die verarbeiteten Teilschritte (processed count) zu melden. Hierüber kann der Core sowohl erkennen wenn ein Modul abgestürzt ist (= keine Änderungen am Progress mehr) als auch Ausführungszeiten, Durchsatz et cetera protokollieren. Auch die Frage des letzten Abschnitts ist somit beantwortet: Module können in Abhängigkeit des Fortschritts ihrer Geschwistermodule im gleichen Teilschritt gestartet werden (sodass, wie im obigen Beispiel, die CSV-Datei erst verarbeitet wird, nachdem sie aus der Zip-Datei entpackt wurde).