ETL mit Spring Cloud-Datenfluss

ETL mit Spring Cloud-Datenfluss

1. Überblick

Spring Cloud Data Flow ist ein Cloud-natives Toolkit zum Erstellen von Echtzeit-Datenpipelines und Batch-Prozessen. Spring Cloud Data Flow kann für eine Reihe von Anwendungsfällen der Datenverarbeitung verwendet werden, z. B. einfacher Import / Export, ETL-Verarbeitung, Ereignis-Streaming und prädiktive Analyse.

In diesem Tutorial lernen wir ein Beispiel für ETL (Extract Transform and Load) in Echtzeit mithilfe einer Stream-Pipeline, die Daten aus einer JDBC-Datenbank extrahiert, in einfache POJOs transformiert und in eine MongoDB lädt.

2. ETL- und Event-Stream-Verarbeitung

ETL - Extrahieren, Transformieren und Laden - wurde allgemein als Prozess bezeichnet, bei dem Daten aus mehreren Datenbanken und Systemen in einem Stapel in ein gemeinsames Data Warehouse geladen werden. In diesem Data Warehouse ist es möglich, umfangreiche Datenanalyseverarbeitungen durchzuführen, ohne die Gesamtleistung des Systems zu beeinträchtigen.

Neue Trends verändern jedoch die Art und Weise, wie dies getan wird. ETL spielt weiterhin eine Rolle bei der Übertragung von Daten in Data Warehouses und Data Lakes.

Heutzutage kann dies mitstreams in an event-stream architecture with the help of Spring Cloud Data Flow geschehen.

3. Datenfluss der Frühlingswolke

Mit Spring Cloud Data Flow (SCDF) können Entwickler Daten-Pipelines in zwei Varianten erstellen:

  • Langlebige Echtzeit-Stream-Anwendungen mit Spring Cloud Stream

  • Kurzlebige Batch-Task-Anwendungen mit Spring Cloud Task

In diesem Artikel behandeln wir die erste, langlebige Streaming-Anwendung, die auf Spring Cloud Stream basiert.

3.1. Spring Cloud Stream-Anwendungen

Die SCDF-Stream-Pipelines bestehen aus Schritten,whereeach step is an application built in Spring Boot style using the Spring Cloud Stream micro-framework.. Diese Anwendungen werden von einer Messaging-Middleware wie Apache Kafka oder RabbitMQ integriert.

Diese Anwendungen sind in Quellen, Prozessoren und Senken unterteilt. Im Vergleich zum ETL-Prozess können wir sagen, dass die Quelle der „Extrakt“ ist, der Prozessor der „Transformator“ und die Senke der „Last“ -Teil.

In einigen Fällen können wir einen Anwendungsstarter in einem oder mehreren Schritten der Pipeline verwenden. Dies bedeutet, dass wir für einen Schritt keine neue Anwendung implementieren müssen, sondern stattdessen einen bereits implementierten vorhandenen Anwendungsstarter konfigurieren müssen.

Eine Liste der Anwendungsstarter konntehere gefunden werden.

3.2. Spring Cloud-Datenflussserver

The last piece of the architecture is the Spring Cloud Data Flow Server. Der SCDF-Server stellt die Anwendungen und den Pipeline-Stream mithilfe der Spring Cloud Deployer-Spezifikation bereit. Diese Spezifikation unterstützt die Cloud-native SCDF-Variante, indem sie für eine Reihe moderner Laufzeiten wie Kubernetes, Apache Mesos, Yarn und Cloud Foundry bereitgestellt wird.

Wir können den Stream auch als lokale Bereitstellung ausführen.

Weitere Informationen zur SCDF-Architektur finden Sie unterhere.

4. Umgebungs-Setup

Bevor wir beginnen, müssen wirchoose the pieces of this complex deployment. Das erste zu definierende Teil ist der SCDF-Server.

Zum Testenwe’ll use SCDF Server Local for local development. Für die Produktionsbereitstellung können wir später eine Cloud-native Laufzeit wieSCDF Server Kubernetes auswählen. Wir können die Liste der Serverlaufzeitenhere finden.

Lassen Sie uns nun die Systemanforderungen überprüfen, um diesen Server auszuführen.

4.1. System Anforderungen

Um den SCDF-Server auszuführen, müssen zwei Abhängigkeiten definiert und eingerichtet werden:

  • die Messaging-Middleware und

  • das RDBMS.

Für die Messaging-Middlewarewe’ll work with RabbitMQ, and we choose PostgreSQL as an RDBMS zum Speichern unserer Pipeline-Stream-Definitionen.

Laden Sie zum Ausführen von RabbitMQ die neueste Versionhere herunter und starten Sie eine RabbitMQ-Instanz mit der Standardkonfiguration oder führen Sie den folgenden Docker-Befehl aus:

docker run --name dataflow-rabbit -p 15672:15672 -p 5672:5672 -d rabbitmq:3-management

Installieren Sie als letzten Installationsschritt das PostgreSQL-RDBMS und führen Sie es auf dem Standardport 5432 aus. Erstellen Sie anschließend eine Datenbank, in der SCDF die Stream-Definitionen mithilfe des folgenden Skripts speichern kann:

CREATE DATABASE dataflow;

4.2. Spring Cloud-Datenflussserver lokal

Zum Ausführen des SCDF-Serverlokals können Sie den Serverusing docker-compose, oder als Java-Anwendung starten.

Here, we’ll run the SCDF Server Local as a Java application. Um die Anwendung zu konfigurieren, müssen wir die Konfiguration als Java-Anwendungsparameter definieren. Wir benötigen Java 8 im Systempfad.

Um die JARs und Abhängigkeiten zu hosten, müssen wir einen Basisordner für unseren SCDF-Server erstellen und die lokale SCDF-Server-Distribution in diesen Ordner herunterladen. Sie können die neueste Distribution von SCDF Server Localhere herunterladen.

Außerdem müssen wir einen lib-Ordner erstellen und dort einen JDBC-Treiber ablegen. Die neueste Version des PostgreSQL-Treibers isthere verfügbar.

Lassen Sie uns abschließend den lokalen SCDF-Server ausführen:

$java -Dloader.path=lib -jar spring-cloud-dataflow-server-local-1.6.3.RELEASE.jar \
    --spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/dataflow \
    --spring.datasource.username=postgres_username \
    --spring.datasource.password=postgres_password \
    --spring.datasource.driver-class-name=org.postgresql.Driver \
    --spring.rabbitmq.host=127.0.0.1 \
    --spring.rabbitmq.port=5672 \
    --spring.rabbitmq.username=guest \
    --spring.rabbitmq.password=guest

Anhand dieser URL können wir überprüfen, ob es ausgeführt wird:

4.3. Spring Cloud-Datenfluss-Shell

Die SCDF-Shell istcommand line tool that makes it easy to compose and deploy our applications and pipelines. Diese Shell-Befehle werden über den Spring Cloud Data Flow ServerREST APIausgeführt.

Laden Sie die neueste Version des JAR in Ihren SCDF-Home-Ordner herunter, verfügbarhere. Führen Sie anschließend den folgenden Befehl aus (aktualisieren Sie die Version nach Bedarf):

$ java -jar spring-cloud-dataflow-shell-1.6.3.RELEASE.jar
  ____                              ____ _                __
 / ___| _ __  _ __(_)_ __   __ _   / ___| | ___  _   _  __| |
 \___ \| '_ \| '__| | '_ \ / _` | | |   | |/ _ \| | | |/ _` |
  ___) | |_) | |  | | | | | (_| | | |___| | (_) | |_| | (_| |
 |____/| .__/|_|  |_|_| |_|\__, |  \____|_|\___/ \__,_|\__,_|
  ____ |_|    _          __|___/                 __________
 |  _ \  __ _| |_ __ _  |  ___| | _____      __  \ \ \ \ \ \
 | | | |/ _` | __/ _` | | |_  | |/ _ \ \ /\ / /   \ \ \ \ \ \
 | |_| | (_| | || (_| | |  _| | | (_) \ V  V /    / / / / / /
 |____/ \__,_|\__\__,_| |_|   |_|\___/ \_/\_/    /_/_/_/_/_/


Welcome to the Spring Cloud Data Flow shell. For assistance hit TAB or type "help".
dataflow:>

Wenn anstelle von "dataflow:>”" in der letzten Zeile "server-unknown:>”" angezeigt wird, wird der SCDF-Server nicht bei localhost ausgeführt. Führen Sie in diesem Fall den folgenden Befehl aus, um eine Verbindung zu einem anderen Host herzustellen:

server-unknown:>dataflow config server http://{host}

Jetzt ist Shell mit dem SCDF-Server verbunden und wir können unsere Befehle ausführen.

Das erste, was wir in Shell tun müssen, ist, die Anwendungsstarter zu importieren. Suchen Sie die neueste Versionhere für RabbitMQ + Maven in Spring Boot 2.0.x und führen Sie den folgenden Befehl aus (aktualisieren Sie die Version erneut, hier "Darwin-SR1", falls erforderlich):

$ dataflow:>app import --uri http://bit.ly/Darwin-SR1-stream-applications-rabbit-maven

Führen Sie zum Überprüfen der installierten Anwendungen den folgenden Shell-Befehl aus:

$ dataflow:> app list

Infolgedessen sollte eine Tabelle mit allen installierten Anwendungen angezeigt werden.

Außerdem bietet SCDF eine grafische Oberfläche mit dem NamenFlo, auf die wir über die folgende Adresse zugreifen können:http://localhost:9393/dashboard. Die Verwendung fällt jedoch nicht in den Geltungsbereich dieses Artikels.

5. ETL-Pipeline erstellen

Erstellen wir jetzt unsere Stream-Pipeline. Dazu verwenden wir den Starter der JDBC-Quellanwendung, um Informationen aus unserer relationalen Datenbank zu extrahieren.

Außerdem erstellen wir einen benutzerdefinierten Prozessor zum Transformieren der Informationsstruktur und eine benutzerdefinierte Senke zum Laden unserer Daten in eine MongoDB.

5.1. Extract - Vorbereiten einer relationalen Datenbank für die Extraktion

Erstellen wir eine Datenbank mit dem Namencrm und eine Tabelle mit dem Namencustomer:

CREATE DATABASE crm;
CREATE TABLE customer (
    id bigint NOT NULL,
    imported boolean DEFAULT false,
    customer_name character varying(50),
    PRIMARY KEY(id)
)

Beachten Sie, dass wir ein Flagimported verwenden, in dem gespeichert wird, welcher Datensatz bereits importiert wurde. Bei Bedarf können wir diese Informationen auch in einer anderen Tabelle speichern.

Fügen wir nun einige Daten ein:

INSERT INTO customer(id, customer_name, imported) VALUES (1, 'John Doe', false);

5.2. Transformieren - Zuordnen von JDBC-Feldern zur MongoDB-Feldstruktur

Für den Transformationsschritt führen wir eine einfache Übersetzung des Feldscustomer_name aus der Quelltabelle in ein neues Feldname durch. Andere Transformationen könnten hier durchgeführt werden, aber lassen Sie uns das Beispiel kurz halten.

To do this, we’ll create a new project with the name customer-transform. Der einfachste Weg, dies zu tun, ist die Verwendung der SiteSpring Initializr zum Erstellen des Projekts. Wählen Sie nach dem Aufrufen der Website eine Gruppe und einen Artefaktnamen aus. Wir verwendencom.customer bzw.customer-transform,.

Klicken Sie anschließend auf die Schaltfläche „Projekt generieren“, um das Projekt herunterzuladen. Entpacken Sie dann das Projekt, importieren Sie es in Ihre bevorzugte IDE und fügen Sie denpom.xml die folgende Abhängigkeit hinzu:


    org.springframework.cloud
    spring-cloud-stream-binder-rabbit

Jetzt können wir mit der Codierung der Feldnamenkonvertierung beginnen. Dazu erstellen wir dieCustomer-Klasse als Adapter. Diese Klasse empfängtcustomer_name über die MethodesetName() und gibt ihren Wert über die MethodegetName. aus

Die@JsonProperty -Sannotationen führen die Transformation durch, während sie von JSON nach Java deserialisieren:

public class Customer {

    private Long id;

    private String name;

    @JsonProperty("customer_name")
    public void setName(String name) {
        this.name = name;
    }

    @JsonProperty("name")
    public String getName() {
        return name;
    }

    // Getters and Setters
}

Der Prozessor muss Daten von einer Eingabe empfangen, die Transformation durchführen und das Ergebnis an einen Ausgabekanal binden. Erstellen wir dazu eine Klasse:

import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Processor;
import org.springframework.integration.annotation.Transformer;

@EnableBinding(Processor.class)
public class CustomerProcessorConfiguration {

    @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
    public Customer convertToPojo(Customer payload) {

        return payload;
    }
}

Im obigen Code können wir beobachten, dass die Transformation automatisch erfolgt. Die Eingabe empfängt die Daten, während JSON und Jackson sie mit den Methodenset in einCustomer-Objekt deserialisieren.

Das Gegenteil ist für die Ausgabe, die Daten werden mit den Methodenget nach JSON serialisiert.

5.3. Laden - Sinken in MongoDB

Ähnlich wie beim Transformationsschrittwe’ll create another maven project, now with the name customer-mongodb-sink. Greifen Sie erneut aufSpring Initializr zu, wählen Sie für die Gruppecom.customer und für das Artefaktcustomer-mongodb-sink. Geben Sie dannMongoDB * “* in das Suchfeld für Abhängigkeiten ein und laden Sie das Projekt herunter.

Als nächstes entpacken und in Ihre Lieblings-IDE importieren.

Fügen Sie dann dieselbe zusätzliche Abhängigkeit wie im Projektcustomer-transformhinzu.

Jetzt erstellen wir eine weitereCustomer-Klasse, um in diesem Schritt Eingaben zu erhalten:

import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection="customer")
public class Customer {

    private Long id;
    private String name;

    // Getters and Setters
}

Zum Senken derCustomer erstellen wir eine Listener-Klasse, die die Kundenentität mit denCustomerRepository speichert:

@EnableBinding(Sink.class)
public class CustomerListener {

    @Autowired
    private CustomerRepository repository;

    @StreamListener(Sink.INPUT)
    public void save(Customer customer) {
        repository.save(customer);
    }
}

Und dasCustomerRepository ist in diesem Fall einMongoRepository aus Spring Data:

import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CustomerRepository extends MongoRepository {

}

5.4. Stream-Definition

Jetztboth custom applications are ready to be registered on SCDF Server. Um dies zu erreichen, kompilieren Sie beide Projekte mit dem Maven-Befehlmvn install.

Wir registrieren sie dann mit der Spring Cloud Data Flow Shell:

app register --name customer-transform --type processor --uri maven://com.customer:customer-transform:0.0.1-SNAPSHOT
app register --name customer-mongodb-sink --type sink --uri maven://com.customer:customer-mongodb-sink:jar:0.0.1-SNAPSHOT

Überprüfen Sie abschließend, ob die Anwendungen in SCDF gespeichert sind, und führen Sie den Befehl application list in der Shell aus:

app list

Daher sollten wir beide Anwendungen in der resultierenden Tabelle sehen.

5.4.1. Domänenspezifische Sprache für Stream-Pipeline - DSL

Ein DSL definiert die Konfiguration und den Datenfluss zwischen den Anwendungen. Das SCDF DSL ist einfach. Im ersten Wort definieren wir den Namen der Anwendung, gefolgt von den Konfigurationen.

Die Syntax ist ein von Unix inspiriertesPipeline syntax, das vertikale Balken, auch als "Pipes" bezeichnet, verwendet, um mehrere Anwendungen zu verbinden:

http --port=8181 | log

Dadurch wird eine HTTP-Anwendung erstellt, die an Port 8181 bereitgestellt wird und empfangene Body-Payloads an ein Protokoll sendet.

Lassen Sie uns nun sehen, wie Sie die DSL-Stream-Definition der JDBC-Quelle erstellen.

5.4.2. JDBC-Quelldatenstromdefinition

Die Schlüsselkonfigurationen für die JDBC-Quelle sindquery undupdate. query will select unread records while update will change a flag to prevent the current records from being reread.

Außerdem definieren wir die JDBC-Quelle so, dass sie mit einer festen Verzögerung von 30 Sekunden und maximal 1000 Zeilen abgefragt wird. Schließlich definieren wir die Verbindungskonfigurationen wie Treiber, Benutzername, Kennwort und Verbindungs-URL:

jdbc 
    --query='SELECT id, customer_name FROM public.customer WHERE imported = false'
    --update='UPDATE public.customer SET imported = true WHERE id in (:id)'
    --max-rows-per-poll=1000
    --fixed-delay=30 --time-unit=SECONDS
    --driver-class-name=org.postgresql.Driver
    --url=jdbc:postgresql://localhost:5432/crm
    --username=postgres
    --password=postgres

Weitere JDBC-Quellkonfigurationseigenschaften finden Sie inhere.

5.4.3. Kunden MongoDB Sink Stream Definition

Da wir die Verbindungskonfigurationen nicht inapplication.properties voncustomer-mongodb-sink definiert haben, werden wir sie über DSL-Parameter konfigurieren.

Unsere Anwendung basiert vollständig aufMongoDataAutoConfiguration.. Sie können die anderen möglichen Konfigurationenhere. überprüfen. Grundsätzlich definieren wir diespring.data.mongodb.uri:

customer-mongodb-sink --spring.data.mongodb.uri=mongodb://localhost/main

5.4.4. Erstellen Sie den Stream und stellen Sie ihn bereit

Um die endgültige Stream-Definition zu erstellen, kehren Sie zunächst zur Shell zurück und führen Sie den folgenden Befehl aus (ohne Zeilenumbrüche, die gerade zur besseren Lesbarkeit eingefügt wurden):

stream create --name jdbc-to-mongodb
  --definition "jdbc
  --query='SELECT id, customer_name FROM public.customer WHERE imported=false'
  --fixed-delay=30
  --max-rows-per-poll=1000
  --update='UPDATE customer SET imported=true WHERE id in (:id)'
  --time-unit=SECONDS
  --password=postgres
  --driver-class-name=org.postgresql.Driver
  --username=postgres
  --url=jdbc:postgresql://localhost:5432/crm | customer-transform | customer-mongodb-sink
  --spring.data.mongodb.uri=mongodb://localhost/main"

Dieses Stream-DSL definiert einen Stream mit dem Namen jdbc-to-mongodb. Als nächsteswe’ll deploy the stream by its name:

stream deploy --name jdbc-to-mongodb

Schließlich sollten wir die Speicherorte aller verfügbaren Protokolle in der Protokollausgabe sehen:

Logs will be in {PATH_TO_LOG}/spring-cloud-deployer/jdbc-to-mongodb/jdbc-to-mongodb.customer-mongodb-sink

Logs will be in {PATH_TO_LOG}/spring-cloud-deployer/jdbc-to-mongodb/jdbc-to-mongodb.customer-transform

Logs will be in {PATH_TO_LOG}/spring-cloud-deployer/jdbc-to-mongodb/jdbc-to-mongodb.jdbc

6. Fazit

In diesem Artikel sehen wir ein vollständiges Beispiel einer ETL-Datenpipeline mit Spring Cloud Data Flow.

Am bemerkenswertesten ist, dass wir die Konfigurationen eines Anwendungsstarters gesehen, eine ETL-Stream-Pipeline mit der Spring Cloud Data Flow Shell erstellt und benutzerdefinierte Anwendungen für das Lesen, Transformieren und Schreiben von Daten implementiert haben.

Der Beispielcode befindet sich wie immer inin the GitHub project.