Apache Flink – Wie Data Streaming die Datenverarbeitung revolutioniert
Data Streaming und Stream Processing, die unmittelbare Verarbeitung von Datenströmen, gewinnen in der digitalen Geschäftswelt an Bedeutung. Jüngstes Erfolgsbeispiel ist Apache Flink. Nahezu alle geschäftsrelevanten Daten werden als Ereignisstrom erzeugt. Sensormessungen, Website-Klicks, Interaktionen mit mobilen Anwendungen, Datenbankänderungen, Anwendungs- und Maschinenprotokolle, Aktienhandel und Finanztransaktionen. All diese Vorgänge sind durch kontinuierlich generierte Daten gekennzeichnet. Tatsächlich gibt es nur sehr wenige gebundene Datensätze, die auf einmal erzeugt werden, anstatt aus einem Datenstrom aufgezeichnet zu werden.
Typische Herausforderungen der traditionellen Art des Datenzugriffs und der Datenverarbeitung:
- Anwendungsfälle, in denen sich die Daten schneller ändern als die Verarbeitungslogik.
- Anwendungsfälle, in denen sich der Code oder die Anfrage schneller ändert als die Daten.
Während wir es im ersten Szenario mit einem Stream-Processing-Problem zu tun haben, deutet der letztgenannte Fall auf ein Datenexplorationsproblem hin. Beispiele für Anwendungsfälle der Datenexploration sind Offline-Datenanalyse, Data Mining und Aufgaben der Datenwissenschaft. Die klare Trennung von Datenstrom- und Datenexplorationsproblemen führt zu der Erkenntnis, dass die Mehrheit aller Produktionsanwendungen sich mit Problemen der Datenstromverarbeitung befasst. Allerdings werden heute nur wenige davon mit der Stream-Processing-Technologie realisiert.
Beispielsweise gibt es viele Anwendungen, die Daten kontinuierlich analysieren, indem sie laufend produzierte Daten (in einer Datenbank oder einem Data Lake) sammeln und periodisch Batch-Jobs auf den aufgezeichneten Daten ausführen. Die Ergebnisse von Datenexplorationsaufgaben, wie maschinelle Lern-Pipelines oder Berichtsabfragen, werden oft Teil von Streaming-Anwendungen, wenn sie in einer Produktionsumgebung eingesetzt werden. Probleme bei der Stream-Verarbeitung beschränken sich ebenfalls nicht nur auf analytische Anwendungen. Jede Art von Anwendung, die kontinuierlich Eingangsereignisse verarbeitet, adressiert im Wesentlichen ein Streaming-Problem, wie z. B. viele Anwendungen, die als Microservices implementiert sind.
Stateful Stream Processing: Zustandsorientierte Stream-Verarbeitung
Stateful Stream Processing ist ein Unterbereich des Stream Processing, bei der die Berechnung den Kontextzustand beibehält. Dieser Zustand wird verwendet, um Informationen zu speichern, die aus den zuvor gesichteten Ereignissen abgeleitet wurden.
Praktisch alle Stream-Processing-Anwendungen erfordern eine zustandsorientierte Stream-Verarbeitung:
- Eine Betrugspräventionsanwendung würde die letzten Transaktionen für jede Kreditkarte im Status/Zustand behalten. Jede neue Transaktion wird mit denjenigen im Status verglichen, die als gültig oder betrügerisch gekennzeichnet sind, und der Zustand wird mit dieser Transaktion aktualisiert.
- Eine Online-Empfehlungsanwendung würde Parameter speichern, die die Einstellungen des Benutzers beschreiben. Jede Produktinteraktion erzeugt ein Ereignis, das diese Parameter aktualisiert.
- Ein Microservice, der eine Song-Playlist oder einen Onlineshop-Warenkorb verwaltet, empfängt Ereignisse für jede Benutzerinteraktion mit Songs bzw. Produkten. Der Status/Zustand enthält die Liste aller hinzugefügten Elemente.
Konzeptionell bringt die zustandsorientierte Stream-Verarbeitung die Datenbank- oder Schlüssel/Wert-Speichertabellen und die ereignisgesteuerte/reaktive Anwendungs- oder Analyselogik zusammen zu einer eng integrierten Einheit. Die tiefe Integration zwischen dem Zustand und der Ausführung der Anwendungs-/Analyselogik führt zu deutlichen Vorteilen hinsichtlich Leistung, Skalierbarkeit, Datenkonsistenz und Benutzerfreundlichkeit.
Die zustandsorientierte Stream-Verarbeitung erfordert einen Stream-Prozessor, der die Zustandsverwaltung unterstützt. Dies beinhaltet die Unterstützung für die Verarbeitung von zustandsorientierten Streams, elastische Skalierung von zustandsorientierten Streaming-Programmen, Status-Snapshots (für Versionierung und Anwendungs-Updates) sowie Upgrade- und Schemaevolutionsfunktionen.
Anwendungen von Stateful Stream Processing
Die ersten Open-Source-Stream-Prozessoren waren nicht darauf ausgelegt, den breiten Anwendungsbereich der Stream-Verarbeitung abzudecken. Stattdessen konzentrierten sie sich darauf, die Latenzzeit für analytische Anwendungen auf Kosten der Genauigkeit zu reduzieren. Seitdem hat sich die Stream-Processing-Technologie rasant weiterentwickelt und gilt heute als ausgereift. So können modernste Open-Source-Stream-Prozessoren eine viel breitere Palette von Anwendungsfällen abdecken, darunter präzise Analysen mit niedriger Latenz und ereignisgesteuerte Anwendungen. Neue Anwendungsfälle wurden vor allem durch die erweiterte Unterstützung zweier wichtiger Konzepte, nämlich Zustand und Zeit, ermöglicht. Die Menge an Kontrolle, die ein Stream-Verarbeitungs-Framework über Zustand und Zeit ermöglicht, und die Funktionen, die es zur Verwaltung von Zustand und Zeit bietet, bestimmen direkt, welche Arten von Anwendungen mit dem Framework erstellt und ausgeführt werden können.
Zeit ist ein wichtiger Aspekt der Stream-Verarbeitung, da Streams unbegrenzt sind und kontinuierlich Ereignisse liefern. Daher haben viele Stream-Processing-Anwendungen eine inhärente Zeitsemantik, wie z. B. Anwendungen, die Aggregate in Intervallen erzeugen oder zeitliche Muster auf einer Folge von Ereignissen auswerten. Im Vergleich zu Batch-Verarbeitungsanwendungen, die gebundene Datensätze verarbeiten, können Stream-Processing-Anwendungen oft explizit mit der Zeit umgehen, um die Latenz und Vollständigkeit der Ergebnisse zu beeinflussen.
Stream-Processing-Anwendungen nehmen Ereignisse auf, sobald sie ankommen. Je nach Geschäftslogik müssen Ereignisse oder Zwischenergebnisse zur späteren Verarbeitung gespeichert werden. Jede Art von Daten, die in Erinnerung bleiben, um in Zukunft eine Berechnung durchzuführen, gehört zum Zustand einer Anwendung, in Form einer lokalen Variable in einem regulären Programm. Während das Konzept des Zustands leicht zu verstehen ist, ist die Aufgabe, eine zustandsorientierte Stream-Processing-Anwendung in einem verteilten System auszuführen, schwieriger.
Ausfälle in verteilten Systemen sind bekanntlich allgegenwärtig. Im Gegensatz zu frühen Stream-Prozessoren, die den Zustand als flüchtig behandelten oder in externe Datenspeicher ablegten, halten zustandsorientierte Stream-Prozessoren den Anwendungszustand lokal aufrecht und garantieren dessen Konsistenz. Daher muss ein zustandsorientierter Stream-Prozessor sicherstellen, dass der Zustand einer Anwendung im Fehlerfall nicht verloren geht und konsistent wiederhergestellt wird, nachdem die Anwendung wieder korrekt läuft. Das bedeutet, dass eine Anwendung in der Lage sein muss, die Verarbeitung so fortzusetzen, als wäre der Fehler nie passiert. Um eine gute Skalierbarkeit zu erreichen, muss der Anwendungszustand effektiv auf alle beteiligten Knoten verteilt und sogar neu verteilt werden, wenn die Anwendung skaliert wird. Schließlich müssen alle Zustandsverwaltungsvorgänge effizient durchgeführt werden, da der Anwendungszustand auf mehrere Terabyte an Größe wachsen kann.
Jede Anwendung, die kontinuierlich die gleiche Geschäfts- oder Verarbeitungslogik auf eine Folge von Eingabeereignissen anwendet, löst ein Stream-Problem aus. Solche Anwendungen sind im Geschäftsumfeld allgegenwärtig und die überwiegende Mehrheit von ihnen ist zustandsorientiert. Während nur wenige dieser Anwendungen derzeit mit Stream-Processing-Frameworks implementiert werden, gibt es viele Anwendungsklassen, für die verteilte und zustandsorientierte Stream-Prozessoren einen erheblichen Mehrwert bieten könnten, wie beispielsweise verbesserte Latenzzeiten, höhere Durchsätze, bessere Skalierbarkeit, verbesserte Konsistenz und einfacheres Anwendungslebenszyklus-Management. Zu diesen Anwendungsarten gehören analytische Abfragen, ETL-Jobs und Datenpipelines, aber auch transaktionale Anwendungen und datengesteuerte Microservices.
Stateful Stream Processing wird heute verwendet, um ereignisgesteuerte Backends für Webanwendungen (z. B. soziale Netzwerke), dynamisch konfigurierbare Aggregationspipelines zur Analyse des Verhaltens von Handyspielern und sogar interne und öffentliche Dienste zur Definition von Datenpipelines mit SQL und zur Ausführung von Ad-hoc-SQL-Abfragen auf Datenströmen zu erstellen. Weitere Anwendungsfälle sind die Betrugserkennung bei Finanztransaktionen, die Überwachung und Qualitätsbewertung von Geschäftsprozessen, Erkennung von Sicherheitsverletzungen in Netzwerken, Echtzeit-Dashboards zur Überwachung der Servicequalität von Mobilfunknetzen und die Anomalieerkennung von IoT-Daten.
Apache Flink verarbeitet Datenströme mit hohem Durchsatz bei geringer Latenzzeit
Apache Flink ist ein Stream-Processing-Framework aus dem Open-Source-Umfeld. Entwickelt wurde Flink als Distributed-Data-Prozessor, um zustandsorientierte Berechnungen über Datenströme auszuführen. Flink ist extrem genau in der Datenaufnahme, ist schnell wieder einsatzfähig nach Ausfällen, während der bisherige Zustand erhalten bleibt, und ist hochgradig skalierbar.
Die Runtime von Flink ist optimiert für die Verarbeitung unbegrenzter Datenströme sowie begrenzter Datensätze beliebiger Größe. Flink ist in der Lage, Berechnungen auf Tausende von Kernen zu skalieren und damit Datenströme mit hohem Durchsatz bei geringer Latenzzeit zu verarbeiten. Flink-Anwendungen können für Ressourcenmanager wie Hadoop YARN, Apache Mesos und Kubernetes oder für eigenständige Flink-Cluster bereitgestellt werden. Fehlertoleranz ist ein sehr wichtiger Aspekt von Flink, wie bei jedem verteilten System. Flink kann in einem hochverfügbaren Modus ohne Single Point of Failure arbeiten und zustandsorientierte Anwendungen aus Ausfällen mit genau einmaligen Zustandskonsistenzgarantien wiederherstellen. Darüber hinaus bietet Flink viele Funktionen, um die betrieblichen Aspekte der laufenden Stream-Processing-Anwendungen in der Produktion zu erleichtern. Der Datenprozessor lässt sich problemlos in die bestehende Protokollierungs- und Metrikinfrastruktur integrieren und bietet eine REST-API zum Senden und Steuern laufender Anwendungen.
Flink bietet mehrere APIs mit unterschiedlichen Kompromissen für Ausdruckskraft und Prägnanz bei der Implementierung von Stream-Processing-Anwendungen. Die DataStream-API ist die Basis-API und bietet bekannte Primitive, die in anderen datenparallelen Verarbeitungs-Frameworks wie map, flatMap, split und union zu finden sind. Diese Primitive werden durch gängige Stream-Verarbeitungsoperationen erweitert, wie z. B. Windowed-Aggregationen, Joins und einen Operator für asynchrone Anfragen an externe Datenspeicher.
Die Prozessfunktionen (ProcessFunctions) von Flink sind Low-Level-Schnittstellen, die eine präzise Kontrolle über Zustand und Zeit ermöglichen. So kann beispielsweise eine Prozessfunktion implementiert werden, um jedes empfangene Ereignis in seinem Zustand zu speichern und einen Timer für einen zukünftigen Zeitpunkt zu registrieren. Später, wenn der Timer ausgelöst wird, kann die Funktion das Ereignis und möglicherweise andere Ereignisse aus seinem Zustand abrufen, um eine Berechnung durchzuführen und ein Ergebnis auszugeben. Diese feinkörnige Steuerung von Zustand und Zeit ermöglicht ein breites Anwendungsspektrum.
Schließlich bieten die SQL-Unterstützung und die Tabellen-API von Flink deklarative Schnittstellen zur Spezifikation einheitlicher Abfragen gegen Streaming- und Batch-Quellen. Dies bedeutet, dass die gleiche Abfrage mit der gleichen Semantik auf einem begrenzten Datensatz und einem Strom von Echtzeitereignissen ausgeführt werden kann. Sowohl Prozessfunktionen als auch SQL-Abfragen können nahtlos in die DataStream-API integriert werden, was dem Entwickler maximale Flexibilität bei der Auswahl der richtigen API bietet.
Zusätzlich zu den Kern-APIs von Flink verfügt Flink über domainspezifische Bibliotheken für die Grafikverarbeitung und Analytik sowie für die komplexe Ereignisverarbeitung (Complex Event Processing, CEP). Die CEP-Bibliothek von Flink bietet eine API zur Definition und Auswertung von Mustern auf Ereignisströmen. Diese Muster-API kann verwendet werden, um Prozesse zu überwachen oder Alarme bei unerwarteten Ereignisabläufen auszulösen.
Streaming-Anwendungen laufen nie als isolierte Dienste. Stattdessen müssen sie Ereignisströme aufnehmen und typischerweise auch aussenden. Apache Flink bietet eine umfangreiche Bibliothek von Konnektoren für die am häufigsten verwendeten Stream- und Speichersysteme. Anwendungen können Streams für Apache Kafka und Amazon Kinesis aufnehmen oder veröffentlichen. Streams können auch durch das Lesen von Dateien aufgenommen werden, wie sie in Verzeichnissen erscheinen, oder durch das Schreiben von Ereignissen in Bucket-Dateien persistiert werden. Flink unterstützt eine Reihe verschiedener Dateisysteme, darunter HDFS, S3 und NFS. Darüber hinaus können Flink-Anwendungen Daten über JDBC "versenken", d. h. in eine relationale Datenbank exportieren, oder in Apache Cassandra und Elasticsearch einfügen.
Mittels Flink laufen geschäftskritische Anwendungen in vielen Unternehmen auf der ganzen Welt und in vielen Branchen wie E-Commerce, Telekommunikation, Finanzdienstleistungen, Gaming und Entertainment. Benutzer melden Anwendungen, die auf Tausenden von Kernen laufen, Terabyte von Zustandsdaten pflegen und Milliarden von Ereignissen pro Tag verarbeiten. Die Open-Source-Community, die Flink entwickelt, wächst kontinuierlich und gewinnt neue Nutzer.
Statusverwaltung in Apache Flink
Viele der herausragenden Funktionen von Flink drehen sich um die Handhabung des Anwendungszustands. Flink lokalisiert immer den Zustand und die Berechnung auf derselben Maschine, so dass alle Zustandszugriffe lokal erfolgen. Wie der Status lokal gespeichert und abgerufen wird, hängt vom Status-Backend ab, das für eine Anwendung konfiguriert ist. State Backends sind steckbare Komponenten. Flink bietet Implementierungen, die den Anwendungszustand als Objekte auf dem JVM-Heap oder serialisiert in RocksDB, einer eingebetteten, plattenbasierten Speichermaschine, speichern. Während das heap-basierte Backend eine In-Memory-Performance bietet, ist es durch die Größe des verfügbaren Speichers begrenzt. Im Gegensatz dazu kann das RocksDB-Backend, das den effizienten LSM-basierten Festplattenspeicher von RocksDB nutzt, problemlos viel größere Zustandsgrößen beibehalten.
Konsistenz ist ein grundlegender Aspekt aller Systeme, die Daten speichern. In Streaming-Systemen wird die Konsistenz oft durch die Begriffe "at least once" ("mindestens einmal") und "exactly once" ("genau einmal") klassifiziert, die beschreiben, wie oft Input-Ereignisse im Fehlerfall verarbeitet werden können. Der Fehlertoleranzmechanismus von Flink basiert auf einem ausgeklügelten Algorithmus, der konsistente Checkpoints des Zustands einer Anwendung auf einen persistenten Remote-Speicher kopiert. Während der Ausführung einer Anwendung ruft Flink regelmäßig Checkpoints des Anwendungszustands ab. Im Falle eines Fehlers startet Flink die Anwendung neu und setzt ihren Zustand auf den letzten erfolgreichen Checkpoint zurück, der vom Remote-Speicher geladen wurde. Da ein Checkpoint die Leseposition aller Quellen beinhaltet (vorausgesetzt, die Quellen können zurückgesetzt werden), bleibt der Zustand der Anwendung konsistent, als ob der Fehler nie passiert wäre, d. h. als ob jedes Eingangsereignis genau einmal verarbeitet wurde, obwohl einige Eingangsereignisse möglicherweise zweimal verarbeitet wurden. Für viele Ausgangskonnektoren und Speichersysteme ist Flink auch in der Lage, eine durchgängige, exakt gleiche Semantik zu erreichen.
Da der Anwendungszustand auf mehrere Terabyte an Größe anwachsen kann und kürzere Checkpoint-Intervalle die Zeit bis zur Wiederherstellung nach Ausfällen verkürzen, ist es wichtig, dass der Checkpoint-Mechanismus der Verarbeitung so wenig Aufwand und Latenz wie möglich auferlegt. Flink reduziert den Aufwand für Checkpoints, indem es Status-Backends verwendet, die Checkpoints asynchron und inkrementell ausführen. Wenn ein Checkpoint erfasst wird, erstellt das State Backend einen lokalen Snapshot der Status-Updates seit dem letzten Checkpoint und setzt die Verarbeitung sofort fort. Der lokale Snapshot wird dann asynchron auf den entfernten Speicherort kopiert.
Die Zustandsbehandlungsfunktion, die Flink von anderen Stream-Prozessoren am meisten unterscheidet, sind Savepoints. Savepoints sind die Grundlage für viele Funktionen, die den Lebenszyklus einer zustandsorientierten Anwendung verwalten. Im Wesentlichen ist ein Savepoint eine konsistente Momentaufnahme des Zustands einer Anwendung. Während Checkpoints von Flink ausgelöst und automatisch verworfen werden, sobald sie durch einen neueren Checkpoint ersetzt werden, werden Savepoints vom Benutzer ausgelöst und bleiben unter der Kontrolle des Benutzers. Savepoints werden verwendet, um den Zustand einer Anwendung zu initialisieren, während die Anwendung gestartet wird. Während dies nach dem Zweck von Checkpoints klingt, bietet das Starten einer Anwendung von einem Savepoint aus viel mehr Freiheitsgrade.
So kann beispielsweise eine Anwendung von einem Savepoint aus gestartet werden, der mit einer früheren Version der Anwendung erstellt wurde. Die neue Version kann Fehlerbehebungen oder andere Verbesserungen enthalten und wird alle Ergebnisse reparieren oder verbessern, die seit der Übernahme des Savepoints erzielt wurden. Savepoints können verwendet werden, um eine Anwendung auf eine neuere Flink-Version zu aktualisieren oder auf einen anderen Cluster zu migrieren. Eine Anwendung kann auch skaliert werden, indem sie von einem Savepoint mit mehr oder weniger Rechenressourcen gestartet wird. Schließlich können Savepoints zum Ausführen von A/B-Tests verwendet werden, indem zwei verschiedene Versionen mit gleichem Anfangszustand zum gleichen Zeitpunkt gestartet werden.
Effiziente Umsetzung vieler zustandsorientierter Streaming-Anwendungen
Stateful Stream Processing ist ein generisches Framework, das auf viele Anwendungsfälle im Unternehmen angewendet werden kann. Die zuvor genannten gängigen Anwendungsfälle können mit Stateful-Streaming-Anwendungen effizient umgesetzt werden. Apache Flink ist in der Lage, einen sehr großen Zustand mit genau einmaligen Konsistenzgarantien aufrechtzuerhalten, lokale Zustandszugriffe mit geringer Latenz durchzuführen und den Lebenszyklus von Anwendungen über Savepoints zu verwalten. Dies macht Flink zu einer idealen Lösung, um zustandsorientierte Streaming-Anwendungen zu betreiben.