Kapitel 1. Einführung in die zustandsorientierte Stromverarbeitung
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
Apache Flink ist ein verteilter Stream-Prozessor mit intuitiven und ausdrucksstarken APIs zur Implementierung zustandsabhängiger Stream-Processing-Anwendungen. Er führt solche Anwendungen effizient und fehlertolerant in großem Maßstab aus. Flink trat der Apache Software Foundation im April 2014 als Inkubationsprojekt bei und wurde im Januar 2015 zu einem Top-Level-Projekt. Seit seinen Anfängen hat Flink eine sehr aktive und ständig wachsende Gemeinschaft von Nutzern und Mitwirkenden. Bis heute haben mehr als fünfhundert Personen an Flink mitgewirkt, und es hat sich zu einer der ausgereiftesten Open-Source-Stream-Processing-Engines entwickelt, wie die weite Verbreitung beweist. Flink ist die Grundlage für umfangreiche, geschäftskritische Anwendungen in vielen Firmen und Unternehmen in verschiedenen Branchen und auf der ganzen Welt.
Die Stream-Processing-Technologie wird bei großen und kleinen Unternehmen immer beliebter, weil sie für viele etablierte Anwendungsfälle wie Datenanalyse, ETL und transaktionale Anwendungen hervorragende Lösungen bietet, aber auch neue Anwendungen, Softwarearchitekturen und Geschäftsmöglichkeiten ermöglicht. In diesem Kapitel erörtern wir, warum die zustandsorientierte Datenstromverarbeitung so populär geworden ist, und bewerten ihr Potenzial. Wir beginnen mit einem Überblick über herkömmliche Datenanwendungsarchitekturen und weisen auf ihre Grenzen hin. Anschließend stellen wir Anwendungsdesigns vor, die auf Stateful Stream Processing basieren und viele interessante Eigenschaften und Vorteile gegenüber herkömmlichen Ansätzen aufweisen. Schließlich gehen wir kurz auf die Entwicklung von Open-Source-Stream-Prozessoren ein und helfen dir, eine Streaming-Anwendung auf einer lokalen Flink-Instanz auszuführen.
Traditionelle Dateninfrastrukturen
Daten und Datenverarbeitung sind in Unternehmen schon seit vielen Jahrzehnten allgegenwärtig. Im Laufe der Jahre hat die Sammlung und Nutzung von Daten stetig zugenommen, und die Unternehmen haben Infrastrukturen entwickelt und aufgebaut, um diese Daten zu verwalten. Die traditionelle Architektur, die die meisten Unternehmen einsetzen, unterscheidet zwei Arten der Datenverarbeitung: die Transaktionsverarbeitung und die analytische Verarbeitung. In diesem Abschnitt gehen wir auf beide Arten ein und erläutern, wie sie Daten verwalten und verarbeiten.
Transaktionsverarbeitung
Unternehmen nutzen alle Arten von Anwendungen für ihre täglichen Geschäftsaktivitäten, z. B. ERP-Systeme (Enterprise Resource Planning), CRM-Software (Customer Relationship Management) und webbasierte Anwendungen. Diese Systeme sind in der Regel mit getrennten Ebenen für die Datenverarbeitung (die Anwendung selbst) und die Speicherung der Daten (ein transaktionales Datenbanksystem) konzipiert, wie in Abbildung 1-1 dargestellt.
Anwendungen sind in der Regel mit externen Diensten verbunden oder stehen menschlichen Nutzern gegenüber und verarbeiten kontinuierlich eingehende Ereignisse wie Bestellungen, E-Mails oder Klicks auf einer Website. Wenn ein Ereignis verarbeitet wird, liest eine Anwendung seinen Status oder aktualisiert ihn, indem sie Transaktionen gegen das entfernte Datenbanksystem ausführt. Oft bedient ein Datenbanksystem mehrere Anwendungen, die manchmal auf dieselben Datenbanken oder Tabellen zugreifen.
Dieses Anwendungsdesign kann zu Problemen führen, wenn Anwendungen weiterentwickelt oder skaliert werden müssen. Da mehrere Anwendungen möglicherweise mit derselben Datendarstellung arbeiten oder dieselbe Infrastruktur nutzen, erfordert die Änderung des Schemas einer Tabelle oder die Skalierung eines Datenbanksystems eine sorgfältige Planung und viel Aufwand. Ein neuerer Ansatz, um die enge Bündelung von Anwendungen zu überwinden, ist das Microservices-Designmuster. Microservices sind als kleine, in sich geschlossene und unabhängige Anwendungen konzipiert. Sie folgen der UNIX-Philosophie, die besagt, dass man nur eine einzige Aufgabe erfüllen muss, und zwar eine gute. Komplexere Anwendungen werden erstellt, indem mehrere Microservices miteinander verbunden werden, die nur über standardisierte Schnittstellen wie RESTful HTTP-Verbindungen kommunizieren. Da Microservices streng voneinander entkoppelt sind und nur über klar definierte Schnittstellen kommunizieren, kann jeder Microservice mit einem anderen Technologie-Stack implementiert werden, einschließlich einer Programmiersprache, Bibliotheken und Datenspeichern. Microservices und die gesamte benötigte Software und Dienste werden in der Regel in unabhängigen Containern gebündelt und bereitgestellt. Abbildung 1-2 zeigt eine Microservice-Architektur.
Analytische Verarbeitung
Die Daten, die in den verschiedenen transaktionalen Datenbanksystemen eines Unternehmens gespeichert sind, können wertvolle Einblicke in den Geschäftsbetrieb eines Unternehmens liefern. Die Daten eines Auftragsbearbeitungssystems können zum Beispiel analysiert werden, um das Umsatzwachstum im Laufe der Zeit zu ermitteln, Gründe für verspätete Lieferungen zu finden oder zukünftige Umsätze vorherzusagen, um den Bestand anzupassen. Transaktionsdaten sind jedoch oft über mehrere unverbundene Datenbanksysteme verteilt und sind wertvoller, wenn sie gemeinsam analysiert werden können. Außerdem müssen die Daten oft in ein gemeinsames Format umgewandelt werden.
Anstatt analytische Abfragen direkt auf den transaktionalen Datenbanken auszuführen, werden die Daten in der Regel in ein Data Warehouse ( ) repliziert, einen speziellen Datenspeicher für analytische Abfrage-Workloads. Um ein Data Warehouse zu befüllen, müssen die von den transaktionalen Datenbanksystemen verwalteten Daten in das Data Warehouse kopiert werden. Der Prozess des Kopierens von Daten in das Data Warehouse wird extract-transform-load (ETL) genannt. Ein ETL-Prozess extrahiert Daten aus einer transaktionalen Datenbank, wandelt sie in eine gemeinsame Darstellung um, die Validierung, Wertnormalisierung, Kodierung, Deduplizierung und Schematransformation umfassen kann, und lädt sie schließlich in die analytische Datenbank. ETL-Prozesse können recht komplex sein und erfordern oft technisch ausgefeilte Lösungen, um die Leistungsanforderungen zu erfüllen. ETL-Prozesse müssen regelmäßig durchgeführt werden, damit die Daten im Data Warehouse synchronisiert werden.
Sobald die Daten in das Data Warehouse importiert worden sind, können sie abgefragt und analysiert werden. In der Regel gibt es zwei Arten von Abfragen, die in einem Data Warehouse ausgeführt werden. Die erste Art sind periodische Berichtsabfragen, die geschäftsrelevante Statistiken wie Umsatz, Nutzerwachstum oder Produktionsleistung berechnen. Diese Kennzahlen werden in Berichten zusammengefasst, die dem Management helfen, den Gesamtzustand des Unternehmens zu beurteilen. Die zweite Art sind Ad-hoc-Abfragen, die darauf abzielen, Antworten auf bestimmte Fragen zu geben und geschäftskritische Entscheidungen zu unterstützen, z. B. eine Abfrage zur Erfassung von Umsatzzahlen und Ausgaben für Radiowerbung, um die Wirksamkeit einer Marketingkampagne zu bewerten. Beide Arten von Abfragen werden von einem Data Warehouse im Stapelverarbeitungsmodus ausgeführt, wie in Abbildung 1-3 dargestellt.
Die Komponenten des Apache Hadoop-Ökosystems sind heute fester Bestandteil der IT-Infrastrukturen vieler Unternehmen. Anstatt alle Daten in ein relationales Datenbanksystem einzugeben, werden große Datenmengen wie Logdateien, soziale Medien oder Webklickprotokolle in Hadoops verteiltes Dateisystem (HDFS), S3 oder andere Massendatenspeicher wie Apache HBase geschrieben, die große Speicherkapazitäten zu geringen Kosten bieten. Daten, die sich in solchen Speichern befinden, können mit einer SQL-on-Hadoop-Engine, z. B. Apache Hive, Apache Drill oder Apache Impala, abgefragt und verarbeitet werden. Die Infrastruktur bleibt jedoch im Grunde dieselbe wie bei einer traditionellen Data Warehouse-Architektur.
Stateful Stream Processing
Praktisch werden alle Daten als kontinuierliche Ströme von Ereignissen erzeugt. Denk an Benutzerinteraktionen auf Websites oder in mobilen Apps, Bestellungen, Serverprotokolle oder Sensormessungen - all das sind Ströme von Ereignissen. Tatsächlich ist es schwierig, Beispiele für endliche, vollständige Datensätze zu finden, die alle auf einmal erzeugt werden. Die zustandsbehaftete Datenstromverarbeitung ist ein Anwendungsdesignmuster für die Verarbeitung unbegrenzter Ereignisströme und lässt sich auf viele verschiedene Anwendungsfälle in der IT-Infrastruktur eines Unternehmens anwenden. Bevor wir auf die Anwendungsfälle eingehen, erklären wir kurz, wie Stateful Stream Processing funktioniert.
Jede Anwendung, die einen Strom von Ereignissen verarbeitet und nicht nur triviale Record-at-a-time-Transformationen durchführt, muss zustandsorientiert sein und die Möglichkeit haben, Zwischendaten zu speichern und darauf zuzugreifen. Wenn eine Anwendung ein Ereignis empfängt, kann sie beliebige Berechnungen durchführen, die das Lesen von Daten aus dem Zustand oder das Schreiben von Daten in den Zustand beinhalten. Im Prinzip kann der Status an vielen verschiedenen Orten gespeichert und abgerufen werden, z. B. in Programmvariablen, lokalen Dateien oder eingebetteten oder externen Datenbanken.
Apache Flink speichert den Anwendungsstatus lokal im Speicher oder in einer eingebetteten Datenbank. Da es sich bei Flink um ein verteiltes System handelt, muss der lokale Status vor Ausfällen geschützt werden, um Datenverluste im Falle eines Anwendungs- oder Maschinenausfalls zu vermeiden. Flink gewährleistet dies, indem es regelmäßig einen konsistenten Checkpoint des Anwendungsstatus in eine entfernte und dauerhafte Speicherung schreibt. Zustand, Zustandskonsistenz und der Checkpointing-Mechanismus von Flink werden in den folgenden Kapiteln ausführlicher behandelt.
Stateful Stream Processing Anwendungen nehmen ihre eingehenden Ereignisse oft aus einem Ereignisprotokoll auf. Ein Ereignisprotokoll speichert und verteilt Ereignisströme. Die Ereignisse werden in ein dauerhaftes, nur anhängendes Protokoll geschrieben, was bedeutet, dass die Reihenfolge der geschriebenen Ereignisse nicht geändert werden kann. Ein Stream, der in ein Ereignisprotokoll geschrieben wird, kann viele Male von denselben oder verschiedenen Verbrauchern gelesen werden. Aufgrund der Append-Only-Eigenschaft des Protokolls werden die Ereignisse immer in genau der gleichen Reihenfolge an alle Verbraucher veröffentlicht. Es gibt mehrere Event-Log-Systeme als Open-Source-Software, von denen Apache Kafka das bekannteste ist, oder als integrierte Dienste, die von Cloud-Computing-Providern angeboten werden.
Die Verbindung einer zustandsbehafteten Streaming-Anwendung, die auf Flink läuft, mit einem Eventlog ist aus mehreren Gründen interessant. In dieser Architektur speichert das Ereignisprotokoll die Eingangsereignisse und kann sie in deterministischer Reihenfolge wiedergeben. Im Falle eines Ausfalls stellt Flink eine Stateful-Streaming-Anwendung wieder her, indem es ihren Zustand von einem früheren Checkpoint wiederherstellt und die Leseposition im Eventlog zurücksetzt. Die Anwendung gibt die Eingangsereignisse aus dem Ereignisprotokoll wieder (und spult sie vor), bis sie das Ende des Streams erreicht. Diese Technik wird verwendet, um Fehler zu beheben, kann aber auch genutzt werden, um eine Anwendung zu aktualisieren, Fehler zu beheben und zuvor ausgegebene Ergebnisse zu reparieren, eine Anwendung auf einen anderen Cluster zu migrieren oder A/B-Tests mit verschiedenen Anwendungsversionen durchzuführen.
Wie bereits erwähnt, ist Stateful Stream Processing eine vielseitige und flexible Architektur, die für viele verschiedene Anwendungsfälle genutzt werden kann. Im Folgenden stellen wir drei Klassen von Anwendungen vor, die häufig mit Stateful Stream Processing implementiert werden: (1) ereignisgesteuerte Anwendungen, (2) Datenpipeline-Anwendungen und (3) Datenanalyseanwendungen.
Reale Streaming-Anwendungsfälle und -Einsätze
Wenn du mehr über reale Anwendungsfälle und Einsätze erfahren möchtest, schau dir die Powered By-Seite von Apache Flink sowie die Vortragsaufzeichnungen und Foliendateien der Flink Forward-Präsentationen an.
Wir beschreiben die Anwendungsklassen als unterschiedliche Muster, um die Vielseitigkeit der zustandsbehafteten Stream-Verarbeitung hervorzuheben, aber die meisten realen Anwendungen weisen die Eigenschaften von mehr als einer Klasse auf.
Ereignisgesteuerte Anwendungen
Ereignisgesteuerte Anwendungen sind zustandsabhängige Streaming-Anwendungen, die Ereignisströme aufnehmen und die Ereignisse mit anwendungsspezifischer Geschäftslogik verarbeiten. Je nach Geschäftslogik kann eine ereignisgesteuerte Anwendung Aktionen auslösen, wie z. B. das Senden einer Warnung oder einer E-Mail, oder Ereignisse in einen ausgehenden Ereignisstrom schreiben, der von einer anderen ereignisgesteuerten Anwendung genutzt wird.
Typische Anwendungsfälle für ereignisgesteuerte Anwendungen sind:
-
Echtzeit-Empfehlungen (z. B. für Produktempfehlungen, während Kunden auf der Website eines Einzelhändlers stöbern)
-
Mustererkennung oder komplexe Ereignisverarbeitung (z. B. zur Betrugserkennung bei Kreditkartentransaktionen)
-
Erkennung von Anomalien (z. B. zur Erkennung von Versuchen, in ein Computernetzwerk einzudringen)
Ereignisgesteuerte Anwendungen sind eine Weiterentwicklung der Microservices. Sie kommunizieren über Ereignisprotokolle statt über REST-Aufrufe und speichern Anwendungsdaten als lokalen Zustand, anstatt sie in einen externen Datenspeicher wie eine relationale Datenbank oder einen Key-Value-Store zu schreiben und von dort zu lesen. Abbildung 1-5 zeigt eine Service-Architektur, die aus ereignisgesteuerten Streaming-Anwendungen besteht.
Die Anwendungen in Abbildung 1-5 sind durch Ereignisprotokolle verbunden. Eine Anwendung sendet ihre Ausgaben in ein Ereignisprotokoll und eine andere Anwendung konsumiert die Ereignisse, die die andere Anwendung gesendet hat. Das Ereignisprotokoll entkoppelt Sender und Empfänger und ermöglicht eine asynchrone, nicht blockierende Ereignisübertragung. Jede Anwendung kann zustandsabhängig sein und ihren eigenen Zustand lokal verwalten, ohne auf externe Datenspeicher zuzugreifen. Die Anwendungen können auch einzeln betrieben und skaliert werden.
Ereignisgesteuerte Anwendungen bieten im Vergleich zu transaktionalen Anwendungen oder Microservices mehrere Vorteile. Der lokale Zugriff auf den Status bietet eine sehr gute Leistung im Vergleich zum Lesen und Schreiben von Abfragen auf entfernten Datenspeichern. Skalierung und Fehlertoleranz werden vom Stream-Prozessor übernommen, und durch die Nutzung eines Ereignisprotokolls als Eingabequelle wird die gesamte Eingabe einer Anwendung zuverlässig gespeichert und kann deterministisch wiedergegeben werden. Außerdem kann Flink den Zustand einer Anwendung auf einen früheren Speicherpunkt zurücksetzen, sodass eine Anwendung weiterentwickelt oder skaliert werden kann, ohne ihren Zustand zu verlieren.
Ereignisgesteuerte Anwendungen stellen ziemlich hohe Anforderungen an den Stream-Prozessor, auf dem sie ausgeführt werden. Nicht alle Stream-Prozessoren sind für die Ausführung ereignisgesteuerter Anwendungen gleich gut geeignet. Die Ausdruckskraft der API und die Qualität der Zustandsbehandlung und der Ereigniszeitunterstützung bestimmen die Geschäftslogik, die implementiert und ausgeführt werden kann. Dieser Aspekt hängt von den APIs des Stream-Prozessors ab, von der Art der Zustandsprimitive, die er bietet, und von der Qualität seiner Unterstützung für die Ereigniszeitverarbeitung. Darüber hinaus sind die einmalige Zustandskonsistenz und die Skalierbarkeit einer Anwendung grundlegende Anforderungen für ereignisgesteuerte Anwendungen. Apache Flink erfüllt all diese Kriterien und ist eine sehr gute Wahl für diese Art von Anwendungen.
Daten-Pipelines
Die heutigen IT-Architekturen umfassen viele verschiedene Datenspeicher, wie relationale und spezielle Datenbanksysteme, Ereignisprotokolle, verteilte Dateisysteme, In-Memory-Caches und Suchindizes. Alle diese Systeme speichern Daten in unterschiedlichen Formaten und Datenstrukturen, die für das jeweilige Zugriffsmuster die beste Leistung bieten. Es ist üblich, dass Unternehmen dieselben Daten in mehreren verschiedenen Systemen speichern, um die Leistung der Datenzugriffe zu verbessern. Zum Beispiel können Informationen zu einem Produkt, das in einem Webshop angeboten wird, in einer Transaktionsdatenbank, einem Web-Cache und einem Suchindex gespeichert werden. Durch diese Replikation der Daten müssen die Datenspeicher synchron gehalten werden.
Ein traditioneller Ansatz, um Daten in verschiedenen Speichern zu synchronisieren, sind periodische ETL-Aufträge. Diese erfüllen jedoch nicht die Latenzanforderungen für viele der heutigen Anwendungsfälle. Eine Alternative ist die Verwendung eines Ereignisprotokolls für die Verteilung von Aktualisierungen. Die Aktualisierungen werden in das Ereignisprotokoll geschrieben und von diesem verteilt. Die Verbraucher des Protokolls übernehmen die Aktualisierungen in die betroffenen Datenspeicher. Je nach Anwendungsfall müssen die übertragenen Daten normalisiert, mit externen Daten angereichert oder aggregiert werden, bevor sie in den Zieldatenspeicher übernommen werden.
Das Einlesen, Umwandeln und Einfügen von Daten mit geringer Latenz ist ein weiterer häufiger Anwendungsfall für zustandsabhängige Stream Processing-Anwendungen. Diese Art von Anwendung wird als Datenpipeline bezeichnet. Datenpipelines müssen in der Lage sein, große Datenmengen in kurzer Zeit zu verarbeiten. Ein Stream-Prozessor, der eine Datenpipeline betreibt, sollte außerdem über viele Source- und Sink-Konnektoren verfügen, um Daten von verschiedenen Speichersystemen zu lesen und in diese zu schreiben. Auch das alles kann Flink.
Streaming-Analytik
ETL-Aufträge importieren in regelmäßigen Abständen Daten in einen Datenspeicher und die Daten werden durch Ad-hoc- oder geplante Abfragen verarbeitet. Dabei handelt es sich um Stapelverarbeitung, unabhängig davon, ob die Architektur auf einem Data Warehouse oder Komponenten des Hadoop-Ökosystems basiert. Das regelmäßige Laden von Daten in ein Datenanalysesystem ist zwar seit vielen Jahren Stand der Technik, führt aber zu erheblichen Latenzzeiten in der Analysepipeline.
Je nach den Zeitplanungsintervallen kann es Stunden oder Tage dauern, bis ein Datenpunkt in einen Bericht aufgenommen wird. Bis zu einem gewissen Grad kann die Latenzzeit durch den Import von Daten in den Datenspeicher mit einer Datenpipeline-Anwendung verringert werden. Aber auch bei kontinuierlichem ETL wird es immer eine Verzögerung geben, bis ein Ereignis von einer Abfrage verarbeitet wird. Während diese Verzögerung in der Vergangenheit akzeptabel war, müssen Anwendungen heute in der Lage sein, Daten in Echtzeit zu sammeln und sofort darauf zu reagieren (z. B. indem sie sich an veränderte Bedingungen in einem Handyspiel anpassen oder das Nutzererlebnis für einen Online-Händler personalisieren).
Anstatt darauf zu warten, dass sie regelmäßig ausgelöst wird, nimmt eine Streaming-Analyse-Anwendung kontinuierlich Ereignisströme auf und aktualisiert ihr Ergebnis, indem sie die neuesten Ereignisse mit geringer Latenzzeit einbezieht. Dies ist vergleichbar mit den Wartungstechniken, die Datenbanksysteme zur Aktualisierung von materialisierten Ansichten verwenden. Normalerweise speichern Streaming-Anwendungen ihre Ergebnisse in einem externen Datenspeicher, der effiziente Aktualisierungen unterstützt, z. B. in einer Datenbank oder einem Key-Value-Speicher. Die live aktualisierten Ergebnisse einer Streaming-Analytics-Anwendung können für Dashboard-Anwendungen genutzt werden, wie in Abbildung 1-6 dargestellt.
Neben der viel kürzeren Zeit, die benötigt wird, um ein Ereignis in ein Analyseergebnis einfließen zu lassen, gibt es einen weiteren, weniger offensichtlichen Vorteil von Streaming-Analyseanwendungen. Herkömmliche Analysepipelines bestehen aus mehreren Einzelkomponenten wie einem ETL-Prozess, einem Speichersystem und - im Falle einer Hadoop-basierten Umgebung - einem Datenprozessor und einem Zeitplannungsprogramm zum Auslösen von Aufträgen oder Abfragen. Im Gegensatz dazu kümmert sich ein Stream-Prozessor, der eine zustandsabhängige Streaming-Anwendung ausführt, um all diese Verarbeitungsschritte, einschließlich der Aufnahme von Ereignissen, der kontinuierlichen Berechnung einschließlich der Zustandsverwaltung und der Aktualisierung der Ergebnisse. Außerdem kann der Stream-Prozessor Ausfälle mit einer einmaligen Konsistenzgarantie beheben und die Rechenressourcen einer Anwendung anpassen. Stream-Prozessoren wie Flink unterstützen auch die Verarbeitung von Ereignissen, um korrekte und deterministische Ergebnisse zu erzielen, und können große Datenmengen in kurzer Zeit verarbeiten.
Streaming-Analytics-Anwendungen werden häufig für folgende Zwecke eingesetzt:
-
Überwachung der Qualität von Mobilfunknetzen
-
Analyse des Nutzerverhaltens in mobilen Anwendungen
-
Ad-hoc-Analyse von Live-Daten in der Verbrauchertechnologie
Auch wenn wir es hier nicht behandeln, bietet Flink auch Unterstützung für analytische SQL-Abfragen über Streams.
Die Entwicklung von Open Source Stream Processing
Die Datenstromverarbeitung ist keine neue Technologie. Einige der ersten Forschungsprototypen und kommerziellen Produkte gehen auf die späten 1990er Jahre zurück. Die zunehmende Verbreitung der Stream-Processing-Technologie in der jüngeren Vergangenheit wurde jedoch vor allem durch die Verfügbarkeit ausgereifter Open-Source-Stream-Prozessoren vorangetrieben. Heute treiben verteilte Open-Source-Stream-Prozessoren geschäftskritische Anwendungen in vielen Unternehmen verschiedener Branchen an, z. B. im (Online-)Einzelhandel, in den sozialen Medien, in der Telekommunikation, bei Spielen und im Bankwesen. Open-Source-Software ist eine wichtige Triebfeder für diesen Trend, vor allem aus zwei Gründen:
- Open-Source-Stream-Processing-Software ist eine Ware, die jeder bewerten und nutzen kann.
- Die skalierbare Stream-Processing-Technologie entwickelt sich dank der Bemühungen vieler Open-Source-Communities schnell weiter.
Allein die Apache Software Foundation beherbergt mehr als ein Dutzend Projekte zum Thema Stream Processing. Neue Projekte zur verteilten Datenstromverarbeitung betreten kontinuierlich die Open-Source-Bühne und fordern den Stand der Technik mit neuen Funktionen und Möglichkeiten heraus. Die Open-Source-Gemeinschaften verbessern ständig die Fähigkeiten ihrer Projekte und verschieben die technischen Grenzen der Streamverarbeitung. Wir werfen einen kurzen Blick in die Vergangenheit, um zu sehen, woher die Open-Source-Streamverarbeitung kommt und wo sie heute steht.
Ein bisschen Geschichte
Die erste Generation verteilter Open-Source-Stream-Prozessoren (2011) konzentrierte sich auf die Ereignisverarbeitung mit Latenzen im Millisekundenbereich und bot Garantien gegen den Verlust von Ereignissen bei Ausfällen. Diese Systeme hatten eher einfache APIs und boten keine eingebaute Unterstützung für genaue und konsistente Ergebnisse von Streaming-Anwendungen, da die Ergebnisse vom Timing und der Reihenfolge der ankommenden Ereignisse abhingen. Außerdem konnten Ereignisse, auch wenn sie nicht verloren gingen, mehr als einmal verarbeitet werden. Im Gegensatz zu Batch-Prozessoren tauschten die ersten Open-Source-Stream-Prozessoren die Genauigkeit der Ergebnisse gegen eine bessere Latenzzeit ein. Die Beobachtung, dass Datenverarbeitungssysteme (zu diesem Zeitpunkt) entweder schnelle oder genaue Ergebnisse liefern konnten, führte zur Entwicklung der sogenannten Lambda-Architektur, die in Abbildung 1-7 dargestellt ist.
Die Lambda-Architektur ergänzt die herkömmliche periodische Batch-Verarbeitungsarchitektur um eine Geschwindigkeitsschicht, die von einem Stream-Prozessor mit niedriger Latenz angetrieben wird. Die Daten, die bei der Lambda-Architektur ankommen, werden vom Stream-Prozessor aufgenommen und in die Stapelspeicherung geschrieben. Der Stream-Prozessor berechnet ungefähre Ergebnisse nahezu in Echtzeit und schreibt sie in eine Geschwindigkeitstabelle. Der Stapelprozessor verarbeitet die Daten in der Stapelspeicherung regelmäßig, schreibt die genauen Ergebnisse in eine Stapeltabelle und löscht die entsprechenden ungenauen Ergebnisse aus der Geschwindigkeitstabelle. Die Anwendungen verbrauchen die Ergebnisse, indem sie die ungefähren Ergebnisse aus der Geschwindigkeitstabelle und die genauen Ergebnisse aus der Stapeltabelle zusammenführen.
Die Lambda-Architektur ist nicht mehr auf dem neuesten Stand der Technik, wird aber immer noch an vielen Stellen eingesetzt. Das ursprüngliche Ziel dieser Architektur war es, die hohe Ergebnislatenz der ursprünglichen Batch-Analytics-Architektur zu verbessern. Sie hat jedoch ein paar bemerkenswerte Nachteile. Erstens erfordert sie zwei semantisch gleichwertige Implementierungen der Anwendungslogik für zwei separate Verarbeitungssysteme mit unterschiedlichen APIs. Zweitens sind die vom Stream-Prozessor berechneten Ergebnisse nur annähernd. Drittens ist die Lambda-Architektur schwer einzurichten und zu pflegen.
Die nächste Generation verteilter Open-Source-Stream-Prozessoren (2013) verbesserte die erste Generation und bot bessere Ausfallgarantien und stellte sicher, dass im Falle eines Fehlers jeder Eingabedatensatz das Ergebnis genau einmal beeinflusst. Außerdem entwickelten sich die Programmier-APIs von eher einfachen Bedienerschnittstellen zu High-Level-APIs mit mehr eingebauten Primitiven. Einige Verbesserungen, wie z. B. ein höherer Durchsatz und bessere Ausfallgarantien, gingen jedoch auf Kosten einer Erhöhung der Verarbeitungslatenz von Millisekunden auf Sekunden. Außerdem waren die Ergebnisse immer noch vom Timing und der Reihenfolge der eintreffenden Ereignisse abhängig.
Die dritte Generation der verteilten Open-Source-Stream-Prozessoren (2015) befasst sich mit der Abhängigkeit der Ergebnisse vom Zeitpunkt und der Reihenfolge der eintreffenden Ereignisse. In Kombination mit der "Exact-once"-Failure-Semantik sind die Systeme dieser Generation die ersten Open-Source-Stream-Prozessoren, die konsistente und genaue Ergebnisse berechnen können. Da diese Systeme nur Ergebnisse berechnen, die auf tatsächlichen Daten basieren, können sie auch historische Daten genauso verarbeiten wie "Live"-Daten. Eine weitere Verbesserung war die Aufhebung des Kompromisses zwischen Latenz und Durchsatz. Während frühere Stream-Prozessoren nur entweder einen hohen Durchsatz oder eine niedrige Latenz bieten, sind die Systeme der dritten Generation in der Lage, beide Enden des Spektrums zu bedienen. Die Stream-Prozessoren dieser Generation haben die Lambda-Architektur überflüssig gemacht.
Zusätzlich zu den bisher besprochenen Systemeigenschaften wie Fehlertoleranz, Leistung und Ergebnisgenauigkeit haben Stream-Prozessoren auch kontinuierlich neue betriebliche Funktionen hinzugefügt, wie z. B. hochverfügbare Setups, enge Integration mit Ressourcenmanagern wie YARN oder Kubernetes und die Fähigkeit, Streaming-Anwendungen dynamisch zu skalieren. Weitere Funktionen sind die Möglichkeit, den Anwendungscode zu aktualisieren oder einen Auftrag auf einen anderen Cluster oder eine neue Version des Stream-Prozessors zu migrieren, ohne den aktuellen Status zu verlieren.
Ein kurzer Blick auf Flink
Apache Flink ist ein verteilter Stream-Prozessor der dritten Generation mit einem wettbewerbsfähigen Funktionsumfang. Er bietet eine präzise Stream-Verarbeitung mit hohem Durchsatz und niedriger Latenz im großen Maßstab. Flink zeichnet sich insbesondere durch die folgenden Merkmale aus:
-
Ereigniszeit- und Verarbeitungszeit-Semantik. Die Ereignis-Zeit-Semantik liefert konsistente und genaue Ergebnisse, auch wenn die Ereignisse nicht in der richtigen Reihenfolge auftreten. Die Verarbeitungszeit-Semantik kann für Anwendungen mit sehr geringen Latenzanforderungen verwendet werden.
-
Exakt-einmalige Konsistenzgarantien für den Zustand.
-
Millisekunden Latenzzeit bei der Verarbeitung von Millionen von Ereignissen pro Sekunde. Flink-Anwendungen können so skaliert werden, dass sie auf Tausenden von Kernen laufen.
-
Mehrschichtige APIs mit unterschiedlichen Kompromissen bei der Aussagekraft und Benutzerfreundlichkeit. Dieses Buch behandelt die DataStream-API und die Prozessfunktionen, die Primitive für gängige Stream-Verarbeitungsoperationen wie Windowing und asynchrone Operationen sowie Schnittstellen zur präzisen Steuerung von Zustand und Zeit bieten. Die relationalen APIs von Flink, SQL und die LINQ-ähnliche Table API, werden in diesem Buch nicht behandelt.
-
Konnektoren zu den am häufigsten verwendeten Speichersystemen wie Apache Kafka, Apache Cassandra, Elasticsearch, JDBC, Kinesis und (verteilten) Dateisystemen wie HDFS und S3.
-
Die Fähigkeit, Streaming-Anwendungen rund um die Uhr und mit sehr geringen Ausfallzeiten zu betreiben, ist auf die hohe Verfügbarkeit (kein Single Point of Failure), die enge Integration mit Kubernetes, YARN und Apache Mesos, die schnelle Wiederherstellung nach Ausfällen und die Fähigkeit, Aufträge dynamisch zu skalieren, zurückzuführen.
-
Die Möglichkeit, den Anwendungscode von Aufträgen zu aktualisieren und Aufträge auf verschiedene Flink-Cluster zu migrieren, ohne den Status der Anwendung zu verlieren.
-
Detaillierte und anpassbare Erfassung von System- und Anwendungsmetriken, um Probleme frühzeitig zu erkennen und darauf zu reagieren.
-
Und nicht zuletzt ist Flink auch ein vollwertiger Batch-Prozessor.1
Zusätzlich zu diesen Funktionen ist Flink aufgrund seiner benutzerfreundlichen APIs ein sehr entwicklerfreundliches Framework. Der eingebettete Ausführungsmodus startet eine Anwendung und das gesamte Flink-System in einem einzigen JVM-Prozess, der zum Ausführen und Debuggen von Flink-Aufträgen innerhalb einer IDE verwendet werden kann. Diese Funktion ist beim Entwickeln und Testen von Flink-Anwendungen sehr nützlich.
Deine erste Flink-Anwendung ausführen
Unter führen wir dich durch den Prozess des Startens eines lokalen Clusters und der Ausführung einer Streaming-Anwendung, um dir einen ersten Einblick in Flink zu geben. Die Anwendung, die wir ausführen werden, konvertiert und aggregiert zufällig generierte Temperatursensorwerte nach Zeit. Für dieses Beispiel muss auf deinem System Java 8 installiert sein. Wir beschreiben die Schritte für eine UNIX-Umgebung, aber wenn du mit Windows arbeitest, empfehlen wir dir, eine virtuelle Maschine mit Linux, Cygwin (eine Linux-Umgebung für Windows) oder dem Windows Subsystem für Linux einzurichten, das mit Windows 10 eingeführt wurde. Die folgenden Schritte zeigen dir, wie du einen lokalen Flink-Cluster startest und eine Anwendung zur Ausführung übermittelst.
-
Gehe auf die Apache Flink-Webseite und lade die Hadoop-freie Binärdistribution von Apache Flink 1.7.1 für Scala 2.12 herunter.
-
Entpacke die Archivdatei:
$ tar xvfz flink-1.7.1-bin-scala_2.12.tgz
-
Starte einen lokalen Flink-Cluster:
$ cd flink-1.7.1 $ ./bin/start-cluster.sh Starting cluster. Starting standalonesession daemon on host xxx. Starting taskexecutor daemon on host xxx.
-
Öffne die Web-UI von Flink, indem du die URL
http://localhost:8081
in deinem Browser eingibst. Wie in Abbildung 1-8 dargestellt, siehst du einige Statistiken über den lokalen Flink-Cluster, den du gerade gestartet hast. Es wird angezeigt, dass ein einziger TaskManager (die Worker-Prozesse von Flink) verbunden ist und dass ein einziger Task-Slot (Ressourceneinheiten, die von einem TaskManager bereitgestellt werden) verfügbar ist. -
Lade die JAR-Datei herunter, die die Beispiele in diesem Buch enthält:
$ wget https://streaming-with-flink.github.io/\ examples/download/examples-scala.jar
Hinweis
Du kannst die JAR-Datei auch selbst erstellen, indem du die Schritte in der README-Datei des Repositorys befolgst.
-
Führe das Beispiel auf deinem lokalen Cluster aus, indem du die Einstiegsklasse der Anwendung und die JAR-Datei angibst:
$ ./bin/flink run \ -c io.github.streamingwithflink.chapter1.AverageSensorReadings \ examples-scala.jar Starting execution of program Job has been submitted with JobID cfde9dbe315ce162444c475a08cf93d9
-
Sieh dir das Web-Dashboard an. Du solltest einen Auftrag unter "Laufende Aufträge" sehen. Wenn du auf diesen Auftrag klickst, siehst du den Datenfluss und die Live-Metriken der Betreiber des laufenden Auftrags, ähnlich wie in Abbildung 1-9.
-
Die Ausgabe des Auftrags wird in den Standard-Output des Worker-Prozesses von Flink geschrieben, der standardmäßig in eine Datei im Ordner ./log umgeleitet wird. Du kannst die ständig produzierte Ausgabe mit dem Befehl
tail
wie folgt überwachen:$ tail -f ./log/flink-<user>-taskexecutor-<n>-<hostname>.out
Du solltest sehen, dass Zeilen wie diese in die Datei geschrieben werden:
SensorReading(sensor_1,1547718199000,35.80018327300259) SensorReading(sensor_6,1547718199000,15.402984393403084) SensorReading(sensor_7,1547718199000,6.720945201171228) SensorReading(sensor_10,1547718199000,38.101067604893444)
Das erste Feld der
SensorReading
ist einesensorId
, das zweite Feld ist der Zeitstempel in Millisekunden seit1970-01-01-00:00:00.000
und das dritte Feld ist eine über 5 Sekunden berechnete Durchschnittstemperatur. -
Da es sich um eine Streaming-Anwendung handelt, wird die Anwendung so lange ausgeführt, bis du sie abbrichst. Das kannst du tun, indem du den Auftrag im Web-Dashboard auswählst und oben auf der Seite auf die Schaltfläche Abbrechen klickst.
-
Schließlich solltest du den lokalen Flink-Cluster beenden:
$ ./bin/stop-cluster.sh
Das war's. Du hast gerade deinen ersten lokalen Flink-Cluster installiert und gestartet und dein erstes Flink DataStream API-Programm ausgeführt! Natürlich gibt es noch viel mehr über die Stream-Verarbeitung mit Apache Flink zu lernen, und genau darum geht es in diesem Buch.
Zusammenfassung
In diesem Kapitel haben wir eine Einführung in die zustandsabhängige Stream-Verarbeitung gegeben, ihre Anwendungsfälle diskutiert und einen ersten Blick auf Apache Flink geworfen. Wir begannen mit einer Zusammenfassung der traditionellen Dateninfrastrukturen, der Art und Weise, wie Geschäftsanwendungen üblicherweise entwickelt werden, und der Art und Weise, wie Daten in den meisten Unternehmen heute gesammelt und analysiert werden. Dann stellten wir die Idee der zustandsorientierten Stream-Verarbeitung vor und erklärten, wie sie ein breites Spektrum von Anwendungsfällen abdeckt, das von Geschäftsanwendungen und Microservices bis hin zu ETL und Datenanalyse reicht. Wir haben erörtert, wie sich Open-Source-Stream-Processing-Systeme seit ihren Anfängen in den frühen 2010er Jahren entwickelt haben und wie Stream-Processing zu einer praktikablen Lösung für viele Anwendungsfälle in heutigen Unternehmen wurde. Zum Schluss haben wir einen Blick auf Apache Flink und seine umfangreichen Funktionen geworfen und gezeigt, wie man ein lokales Flink-Setup installiert und eine erste Stream-Processing-Anwendung ausführt.
1 Die Stapelverarbeitungs-API von Flink, die DataSet-API, und ihre Operatoren sind von ihren entsprechenden Streaming-Pendants getrennt. Die Vision der Flink-Community ist es jedoch, die Batch-Verarbeitung als einen Spezialfall der Stream-Verarbeitung zu behandeln - die Verarbeitung von begrenzten Streams. Die Flink-Gemeinschaft ist bestrebt, Flink zu einem System mit einer wirklich vereinheitlichten Batch- und Streaming-API und -Laufzeit weiterzuentwickeln.
Get Stream Processing mit Apache Flink now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.