ETL avec flux de données Spring Cloud

ETL avec flux de données Spring Cloud

1. Vue d'ensemble

Spring Cloud Data Flow est une boîte à outils native du cloud pour créer des pipelines de données en temps réel et des processus par lots. Spring Cloud Data Flow est prêt à être utilisé pour toute une gamme de cas d'utilisation du traitement de données, tels que la simple importation / exportation, le traitement ETL, la diffusion d'événements et les analyses prédictives.

Dans ce didacticiel, nous allons apprendre un exemple d'extraction et de chargement en temps réel (ETL) à l'aide d'un pipeline de flux qui extrait les données d'une base de données JDBC, les transforme en POJO simples et les charge dans un MongoDB.

2. Traitement ETL et flux d'événements

ETL - extraire, transformer et charger - était communément appelé un processus qui charge par lots les données de plusieurs bases de données et systèmes dans un entrepôt de données commun. Dans cet entrepôt de données, il est possible d'effectuer un traitement d'analyse de données intensif sans compromettre les performances globales du système.

Cependant, les nouvelles tendances changent la façon dont cela est fait. ETL a toujours un rôle à jouer dans le transfert de données vers des entrepôts de données et des data lacs.

De nos jours, cela peut être fait avecstreams in an event-stream architecture with the help of Spring Cloud Data Flow.

3. Flux de données Cloud Spring

Avec Spring Data Flow (SCDF), les développeurs peuvent créer des pipelines de données de deux manières différentes:

  • Applications de flux en temps réel de longue durée avec Spring Cloud Stream

  • Applications de tâches par lots de courte durée utilisant Spring Cloud Task

Dans cet article, nous aborderons la première, une application de streaming longue durée basée sur Spring Cloud Stream.

3.1. Applications Spring Cloud Stream

Les pipelines SCDF Stream sont composés d'étapes,whereeach step is an application built in Spring Boot style using the Spring Cloud Stream micro-framework. Ces applications sont intégrées par un middleware de messagerie comme Apache Kafka ou RabbitMQ.

Ces applications sont classées en sources, processeurs et éviers. En comparant avec le processus ETL, nous pourrions dire que la source est «extraite», le processeur est «transformateur» et le puits est la «charge».

Dans certains cas, nous pouvons utiliser un démarreur d’application dans une ou plusieurs étapes du pipeline. Cela signifie que nous n'aurions pas besoin de mettre en œuvre une nouvelle application pour une étape, mais plutôt de configurer un démarreur d'application existant déjà implémenté.

Une liste de démarreurs d'application a pu être trouvéehere.

3.2. Serveur de flux de données Cloud Spring

The last piece of the architecture is the Spring Cloud Data Flow Server. Le serveur SCDF effectue le déploiement des applications et du flux de pipeline à l'aide de la spécification Spring Cloud Deployer. Cette spécification prend en charge la saveur native dans le nuage SCDF en se déployant sur une gamme d’exécutions modernes, telles que Kubernetes, Apache Mesos, Yarn et Cloud Foundry.

Nous pouvons également exécuter le flux en tant que déploiement local.

Plus d'informations sur l'architecture SCDF peuvent être trouvéeshere.

4. Configuration de l'environnement

Avant de commencer, nous devonschoose the pieces of this complex deployment. Le premier élément à définir est le serveur SCDF.

Pour les tests,we’ll use SCDF Server Local for local development. Pour le déploiement de production, nous pouvons choisir ultérieurement un environnement d'exécution cloud natif, commeSCDF Server Kubernetes. Nous pouvons trouver la liste des temps d'exécution du serveurhere.

Maintenant, vérifions la configuration système requise pour exécuter ce serveur.

4.1. Configuration requise

Pour exécuter le serveur SCDF, nous devrons définir et configurer deux dépendances:

  • le middleware de messagerie, et

  • le SGBDR.

Pour le middleware de messagerie,we’ll work with RabbitMQ, and we choose PostgreSQL as an RDBMS pour stocker nos définitions de flux de pipeline.

Pour exécuter RabbitMQ, téléchargez la dernière versionhere et démarrez une instance RabbitMQ en utilisant la configuration par défaut ou exécutez la commande Docker suivante:

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

Lors de la dernière étape de l’installation, installez et exécutez le SGBDR PostgreSQL sur le port 5432 par défaut. Ensuite, créez une base de données sur laquelle SCDF peut stocker ses définitions de flux à l’aide du script suivant:

CREATE DATABASE dataflow;

4.2. Spring Cloud Data Flow Server local

Pour exécuter le serveur SCDF local, nous pouvons choisir de démarrer le serveurusing docker-compose, ou nous pouvons le démarrer en tant qu'application Java.

Here, we’ll run the SCDF Server Local as a Java application. Pour configurer l'application, nous devons définir la configuration en tant que paramètres d'application Java. Nous aurons besoin de Java 8 dans le chemin système.

Pour héberger les fichiers jar et les dépendances, nous devons créer un dossier de base pour notre serveur SCDF et télécharger la distribution SCDF Server Local dans ce dossier. Vous pouvez télécharger la distribution la plus récente de SCDF Server Localhere.

Nous devons également créer un dossier lib et y placer un pilote JDBC. La dernière version du pilote PostgreSQL est disponiblehere.

Enfin, exécutons le serveur local SCDF:

$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

Nous pouvons vérifier s'il fonctionne en regardant cette URL:

4.3. Spring Cloud Shell de flux de données

Le SCDF Shell est uncommand line tool that makes it easy to compose and deploy our applications and pipelines. Ces commandes Shell s'exécutent sur le serveur Spring Cloud Data FlowREST API.

Téléchargez la dernière version du fichier jar dans votre dossier de base SCDF, disponiblehere. Une fois que c'est fait, exécutez la commande suivante (mettez à jour la version si nécessaire):

$ 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:>

Si au lieu de «dataflow:>”, vous obtenez«server-unknown:>” dans la dernière ligne, vous n'exécutez pas le serveur SCDF sur l'hôte local. Dans ce cas, exécutez la commande suivante pour vous connecter à un autre hôte:

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

Maintenant, Shell est connecté au serveur SCDF et nous pouvons exécuter nos commandes.

La première chose à faire dans Shell est d’importer les démarreurs d’application. Trouvez la dernière versionhere pour RabbitMQ + Maven dans Spring Boot 2.0.x, et exécutez la commande suivante (encore une fois, mettez à jour la version, ici «Darwin-SR1», si nécessaire):

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

Pour vérifier les applications installées, exécutez la commande Shell suivante:

$ dataflow:> app list

En conséquence, nous devrions voir un tableau contenant toutes les applications installées.

De plus, SCDF propose une interface graphique, nomméeFlo, à laquelle on peut accéder par cette adresse:http://localhost:9393/dashboard. Cependant, son utilisation n'entre pas dans le cadre de cet article.

5. Composition d'un pipeline ETL

Créons maintenant notre pipeline de flux. Pour ce faire, nous utiliserons le démarreur d'application JDBC Source pour extraire des informations de notre base de données relationnelle.

Nous allons également créer un processeur personnalisé pour transformer la structure de l’information et un récepteur personnalisé pour charger nos données dans un MongoDB.

5.1. Extract - Préparation d'une base de données relationnelle pour l'extraction

Créons une base de données avec le nom decrm et une table avec le nom decustomer:

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

Notez que nous utilisons un indicateurimported, qui stockera quel enregistrement a déjà été importé. Nous pourrions également stocker ces informations dans une autre table, si nécessaire.

Maintenant, insérons quelques données:

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

5.2. Transform - Mappage des champs JDBC à la structure des champs MongoDB

Pour l'étape de transformation, nous allons effectuer une traduction simple du champcustomer_name de la table source vers un nouveau champname. D'autres transformations pourraient être effectuées ici, mais gardons l'exemple court.

To do this, we’ll create a new project with the name customer-transform. La manière la plus simple de le faire est d'utiliser le siteSpring Initializr pour créer le projet. Après avoir atteint le site Web, choisissez un groupe et un nom d'artefact. Nous utiliserons respectivementcom.customer etcustomer-transform,.

Une fois cela fait, cliquez sur le bouton “Générer un projet” pour télécharger le projet. Ensuite, décompressez le projet et importez-le dans votre IDE préféré, et ajoutez la dépendance suivante auxpom.xml:


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

Nous sommes maintenant prêts à commencer à coder la conversion du nom de champ. Pour ce faire, nous allons créer la classeCustomer qui servira d’adaptateur. Cette classe recevra lescustomer_name via la méthodesetName() et affichera sa valeur via la méthodegetName.

Les annotations@JsonProperty effectueront la transformation lors de la désérialisation de JSON vers Java:

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
}

Le processeur doit recevoir les données d'une entrée, effectuer la transformation et lier le résultat à un canal de sortie. Créons une classe pour ce faire:

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;
    }
}

Dans le code ci-dessus, nous pouvons observer que la transformation se produit automatiquement. L'entrée reçoit les données en tant que JSON et Jackson les désérialise en un objetCustomer à l'aide des méthodesset.

Le contraire est pour la sortie, les données sont sérialisées en JSON à l'aide des méthodesget.

5.3. Charge - évier dans MongoDB

De même que l'étape de transformation,we’ll create another maven project, now with the name customer-mongodb-sink. Encore une fois, accédez auxSpring Initializr, pour le groupe choisissezcom.customer, et pour l'artefact choisissezcustomer-mongodb-sink. Ensuite, tapezMongoDB * “* dans la zone de recherche des dépendances et téléchargez le projet.

Ensuite, décompressez-le et importez-le dans votre IDE préféré.

Ensuite, ajoutez la même dépendance supplémentaire que dans le projetcustomer-transform.

Nous allons maintenant créer une autre classeCustomer, pour recevoir l'entrée dans cette étape:

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

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

    private Long id;
    private String name;

    // Getters and Setters
}

Pour recevoir lesCustomer, nous allons créer une classe Listener qui sauvegardera l'entité client à l'aide desCustomerRepository:

@EnableBinding(Sink.class)
public class CustomerListener {

    @Autowired
    private CustomerRepository repository;

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

Et leCustomerRepository, dans ce cas, est unMongoRepository de Spring Data:

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

@Repository
public interface CustomerRepository extends MongoRepository {

}

5.4. Définition du flux

Maintenant,both custom applications are ready to be registered on SCDF Server. Pour ce faire, compilez les deux projets à l'aide de la commande Mavenmvn install.

Nous les enregistrons ensuite à l'aide de 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

Enfin, vérifions si les applications sont stockées dans SCDF, exécutez la commande application list dans le shell:

app list

Par conséquent, nous devrions voir les deux applications dans le tableau résultant.

5.4.1. Langage spécifique à un domaine de pipeline - DSL

Un DSL définit la configuration et le flux de données entre les applications. Le DSL SCDF est simple. Dans le premier mot, nous définissons le nom de l'application, suivi des configurations.

De plus, la syntaxe est unPipeline syntax inspiré d'Unix, qui utilise des barres verticales, également appelées «tubes», pour connecter plusieurs applications:

http --port=8181 | log

Cela crée une application HTTP servie sur le port 8181 qui envoie toute charge utile corporelle reçue à un journal.

Voyons maintenant comment créer la définition de flux DSL de la source JDBC.

5.4.2. Définition du flux source JDBC

Les principales configurations de la source JDBC sontquery etupdate. query will select unread records while update will change a flag to prevent the current records from being reread.

En outre, nous définirons la source JDBC pour interroger dans un délai fixe de 30 secondes et interroger au maximum 1 000 lignes. Enfin, nous définirons les configurations de connexion, telles que le pilote, le nom d'utilisateur, le mot de passe et l'URL de connexion:

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

D'autres propriétés de configuration de la source JDBC peuvent être trouvéeshere.

5.4.3. Définition du flux de réception MongoDB du client

Comme nous n'avons pas défini les configurations de connexion enapplication.properties decustomer-mongodb-sink, nous allons configurer via les paramètres DSL.

Notre application est entièrement basée sur lesMongoDataAutoConfiguration. Vous pouvez consulter les autres configurations possibleshere. En gros, nous allons définir lesspring.data.mongodb.uri:

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

5.4.4. Créer et déployer le flux

Tout d'abord, pour créer la définition finale du flux, revenez au shell et exécutez la commande suivante (sans sauts de ligne, ils viennent d'être insérés pour des raisons de lisibilité):

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"

Ce flux DSL définit un flux nommé jdbc-to-mongodb. Ensuite,we’ll deploy the stream by its name:

stream deploy --name jdbc-to-mongodb

Enfin, nous devrions voir les emplacements de tous les journaux disponibles dans la sortie du journal:

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. Conclusion

Dans cet article, nous avons vu un exemple complet de pipeline de données ETL utilisant Spring Cloud Data Flow.

Fait à noter, nous avons vu les configurations d'un démarreur d'application, créé un pipeline de flux ETL à l'aide de Spring Cloud Data Flow Shell et mis en œuvre des applications personnalisées pour la lecture, la transformation et l'écriture de données.

Comme toujours, l'exemple de code peut être trouvéin the GitHub project.