ETL com fluxo de dados do Spring Cloud
1. Visão geral
Spring Cloud Data Flow é um kit de ferramentas nativo da nuvem para construir pipelines de dados em tempo real e processos em lote. O Spring Cloud Data Flow está pronto para ser usado em uma variedade de casos de uso de processamento de dados, como importação / exportação simples, processamento ETL, fluxo de eventos e análise preditiva.
Neste tutorial, aprenderemos um exemplo de Extract Transform and Load (ETL) em tempo real usando um pipeline de fluxo que extrai dados de um banco de dados JDBC, os transforma em POJOs simples e os carrega em um MongoDB.
2. ETL e processamento de fluxo de eventos
ETL - extrair, transformar e carregar - era comumente referido como um processo que carrega em lote dados de vários bancos de dados e sistemas em um armazém de dados comum. Neste data warehouse, é possível fazer um processamento pesado de análise de dados sem comprometer o desempenho geral do sistema.
No entanto, novas tendências estão mudando a maneira como isso é feito. O ETL ainda tem um papel na transferência de dados para data warehouses e lagos de dados.
Hoje em dia, isso pode ser feito comstreams in an event-stream architecture with the help of Spring Cloud Data Flow.
3. Fluxo de dados do Spring Cloud
Com o Spring Cloud Data Flow (SCDF), os desenvolvedores podem criar pipelines de dados de duas formas:
-
Aplicativos de fluxo em tempo real de longa duração usando o Spring Cloud Stream
-
Aplicativos de tarefas em lote de curta duração usando o Spring Cloud Task
Neste artigo, vamos cobrir o primeiro, um aplicativo de streaming de longa duração baseado no Spring Cloud Stream.
3.1. Aplicativos Spring Stream Stream
Os pipelines SCDF Stream são compostos de etapas,whereeach step is an application built in Spring Boot style using the Spring Cloud Stream micro-framework.. Esses aplicativos são integrados por um middleware de mensagens como Apache Kafka ou RabbitMQ.
Esses aplicativos são classificados em fontes, processadores e sumidouros. Comparando com o processo ETL, poderíamos dizer que a fonte é o "extrato", o processador é o "transformador" e o coletor é a parte "carga".
Em alguns casos, podemos usar um iniciador de aplicativo em uma ou mais etapas do pipeline. Isso significa que não precisaríamos implementar um novo aplicativo para uma etapa, mas, em vez disso, configurar um iniciador de aplicativo existente já implementado.
Uma lista de iniciadores de aplicativos pode ser encontradahere.
3.2. Servidor de fluxo de dados do Spring Cloud
The last piece of the architecture is the Spring Cloud Data Flow Server. O servidor SCDF faz a implantação dos aplicativos e o fluxo do pipeline usando a Especificação do Spring Cloud Deployer. Essa especificação suporta o sabor nativo da nuvem do SCDF implementando em uma variedade de tempos de execução modernos, como Kubernetes, Apache Mesos, Yarn e Cloud Foundry.
Além disso, podemos executar o fluxo como uma implantação local.
Mais informações sobre a arquitetura SCDF podem ser encontradashere.
4. Configuração do ambiente
Antes de começar, precisamoschoose the pieces of this complex deployment. A primeira peça a definir é o servidor SCDF.
Para teste,we’ll use SCDF Server Local for local development. Para a implantação de produção, podemos escolher posteriormente um tempo de execução nativo da nuvem, comoSCDF Server Kubernetes. Podemos encontrar a lista de tempos de execução do servidorhere.
Agora, vamos verificar os requisitos do sistema para executar este servidor.
4.1. Requisitos de sistema
Para executar o SCDF Server, teremos que definir e configurar duas dependências:
-
o middleware de mensagens e
-
o RDBMS.
Para o middleware de mensagens,we’ll work with RabbitMQ, and we choose PostgreSQL as an RDBMS para armazenar nossas definições de fluxo de pipeline.
Para executar o RabbitMQ, baixe a versão mais recenteheree inicie uma instância do RabbitMQ usando a configuração padrão ou execute o seguinte comando do Docker:
docker run --name dataflow-rabbit -p 15672:15672 -p 5672:5672 -d rabbitmq:3-management
Como a última etapa da instalação, instale e execute o RDBMS do PostgreSQL na porta padrão 5432. Depois disso, crie um banco de dados em que o SCDF possa armazenar suas definições de fluxo usando o seguinte script:
CREATE DATABASE dataflow;
4.2. Servidor do Spring Cloud Data Flow Server
Para executar o SCDF Server Local, podemos escolher iniciar o servidorusing docker-compose, ou podemos iniciá-lo como um aplicativo Java.
Here, we’ll run the SCDF Server Local as a Java application. Para configurar o aplicativo, temos que definir a configuração como parâmetros do aplicativo Java. Precisamos do Java 8 no caminho do sistema.
Para hospedar os jars e dependências, precisamos criar uma pasta inicial para o nosso Servidor SCDF e fazer o download da distribuição local do servidor SCDF para essa pasta. Você pode baixar a distribuição mais recente do SCDF Server Localhere.
Além disso, precisamos criar uma pasta lib e colocar um driver JDBC lá. A versão mais recente do driver PostgreSQL está disponívelhere.
Finalmente, vamos executar o servidor 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
Podemos verificar se ele está funcionando olhando para este URL:
4.3. Shell de fluxo de dados do Spring Cloud
O SCDF Shell é umcommand line tool that makes it easy to compose and deploy our applications and pipelines. Esses comandos do Shell são executados no Spring Cloud Data Flow ServerREST API.
Baixe a versão mais recente do jar em sua pasta de início SCDF, disponívelhere. Depois de fazer isso, execute o seguinte comando (atualize a versão conforme necessário):
$ 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:>
Se em vez de “dataflow:>” você obtiver“server-unknown:>” na última linha, você não está executando o SCDF Server em localhost. Nesse caso, execute o seguinte comando para conectar-se a outro host:
server-unknown:>dataflow config server http://{host}
Agora, o Shell está conectado ao servidor SCDF e podemos executar nossos comandos.
A primeira coisa que precisamos fazer no Shell é importar os iniciadores de aplicativos. Encontre a versão mais recentehere para RabbitMQ + Maven no Spring Boot 2.0.x e execute o seguinte comando (novamente, atualize a versão, aqui “Darwin-SR1“, conforme necessário):
$ dataflow:>app import --uri http://bit.ly/Darwin-SR1-stream-applications-rabbit-maven
Para verificar os aplicativos instalados, execute o seguinte comando do Shell:
$ dataflow:> app list
Como resultado, devemos ver uma tabela contendo todos os aplicativos instalados.
Além disso, o SCDF oferece uma interface gráfica, chamadaFlo, que podemos acessar por este endereço:http://localhost:9393/dashboard. No entanto, seu uso não está no escopo deste artigo.
5. Compondo um pipeline de ETL
Vamos agora criar nosso pipeline de stream. Para fazer isso, usaremos o iniciador de aplicativo de origem JDBC para extrair informações de nosso banco de dados relacional.
Além disso, criaremos um processador personalizado para transformar a estrutura de informações e um coletor personalizado para carregar nossos dados em um MongoDB.
5.1. Extrair - Preparando um banco de dados relacional para extração
Vamos criar um banco de dados com o nome decrm e uma tabela com o nome decustomer:
CREATE DATABASE crm;
CREATE TABLE customer (
id bigint NOT NULL,
imported boolean DEFAULT false,
customer_name character varying(50),
PRIMARY KEY(id)
)
Observe que estamos usando um sinalizadorimported, que armazenará qual registro já foi importado. Também podemos armazenar essas informações em outra tabela, se necessário.
Agora, vamos inserir alguns dados:
INSERT INTO customer(id, customer_name, imported) VALUES (1, 'John Doe', false);
5.2. Transform - Mapeando campos JDBC para a estrutura de campos do MongoDB
Para a etapa de transformação, faremos uma tradução simples do campocustomer_name da tabela de origem para um novo camponame. Outras transformações podem ser feitas aqui, mas vamos manter o exemplo curto.
To do this, we’ll create a new project with the name customer-transform. A maneira mais fácil de fazer isso é usando o siteSpring Initializr para criar o projeto. Depois de acessar o site, escolha um nome de Grupo e Artefato. Usaremoscom.customer ecustomer-transform, respectivamente.
Feito isso, clique no botão "Gerar Projeto" para fazer o download do projeto. Em seguida, descompacte o projeto e importe-o em seu IDE favorito e adicione a seguinte dependência aopom.xml:
org.springframework.cloud
spring-cloud-stream-binder-rabbit
Agora estamos prontos para começar a codificar a conversão do nome do campo. Para fazer isso, vamos criar a classeCustomer para atuar como um adaptador. Esta classe receberácustomer_name por meio do métodosetName() e produzirá seu valor por meio do métodogetName.
As@JsonProperty annotations farão a transformação enquanto desserializam de JSON para 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
}
O processador precisa receber dados de uma entrada, fazer a transformação e vincular o resultado a um canal de saída. Vamos criar uma classe para fazer isso:
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;
}
}
No código acima, podemos observar que a transformação ocorre automaticamente. A entrada recebe os dados conforme JSON e Jackson os desserializa em um objetoCustomer usando os métodosset.
O oposto é para a saída, os dados são serializados para JSON usando os métodosget.
5.3. Carregar - Coletor no MongoDB
De forma semelhante à etapa de transformação,we’ll create another maven project, now with the name customer-mongodb-sink. Novamente, acesseSpring Initializr, para o Grupo escolhacom.customer, e para o Artefato escolhacustomer-mongodb-sink. Em seguida, digite“MongoDB * “* na caixa de pesquisa de dependências e baixe o projeto.
Em seguida, descompacte e importe-o para o seu IDE favorito.
Em seguida, adicione a mesma dependência extra que no projetocustomer-transform.
Agora vamos criar outra classeCustomer, para receber a entrada nesta etapa:
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection="customer")
public class Customer {
private Long id;
private String name;
// Getters and Setters
}
Para afundarCustomer, criaremos uma classe Listener que salvará a entidade do cliente usando oCustomerRepository:
@EnableBinding(Sink.class)
public class CustomerListener {
@Autowired
private CustomerRepository repository;
@StreamListener(Sink.INPUT)
public void save(Customer customer) {
repository.save(customer);
}
}
E oCustomerRepository, neste caso, é umMongoRepository do Spring Data:
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CustomerRepository extends MongoRepository {
}
5.4. Definição de Stream
Agora,both custom applications are ready to be registered on SCDF Server. Para fazer isso, compile os dois projetos usando o comando Mavenmvn install.
Em seguida, os registramos usando o 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
Por fim, vamos verificar se os aplicativos estão armazenados no SCDF, execute o comando de lista de aplicativos no shell:
app list
Como resultado, devemos ver os dois aplicativos na tabela resultante.
5.4.1. Linguagem específica de domínio do pipeline de fluxo - DSL
Uma DSL define a configuração e o fluxo de dados entre os aplicativos. O SCDF DSL é simples. Na primeira palavra, definimos o nome do aplicativo, seguido pelas configurações.
Além disso, a sintaxe é inspirada no UnixPipeline syntax, que usa barras verticais, também conhecidas como “tubos”, para conectar vários aplicativos:
http --port=8181 | log
Isso cria um aplicativo HTTP servido na porta 8181 que envia qualquer carga útil do corpo recebido para um log.
Agora, vamos ver como criar a definição de fluxo DSL da fonte JDBC.
5.4.2. Definição de fluxo de origem JDBC
As principais configurações da origem JDBC sãoqueryeupdate. query will select unread records while update will change a flag to prevent the current records from being reread.
Além disso, definiremos a fonte JDBC para pesquisar em um atraso fixo de 30 segundos e pesquisar no máximo 1000 linhas. Por fim, definiremos as configurações de conexão, como driver, nome de usuário, senha e URL de conexão:
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
Mais propriedades de configuração de origem JDBC podem ser encontradashere.
5.4.3. Definição de fluxo de coletor MongoDB do cliente
Como não definimos as configurações de conexão emapplication.properties decustomer-mongodb-sink, vamos configurar por meio de parâmetros DSL.
Nosso aplicativo é totalmente baseado noMongoDataAutoConfiguration. Você pode verificar as outras configurações possíveishere. Basicamente, definiremos ospring.data.mongodb.uri:
customer-mongodb-sink --spring.data.mongodb.uri=mongodb://localhost/main
5.4.4. Criar e implantar o fluxo
Primeiro, para criar a definição final do fluxo, volte ao Shell e execute o seguinte comando (sem quebras de linha, elas foram inseridas apenas para facilitar a leitura):
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"
Este fluxo DSL define um fluxo denominado jdbc-to-mongodb. A seguir,we’ll deploy the stream by its name:
stream deploy --name jdbc-to-mongodb
Por fim, devemos ver os locais de todos os logs disponíveis na saída do log:
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. Conclusão
Neste artigo, vimos um exemplo completo de um pipeline de dados ETL usando Spring Cloud Data Flow.
Mais digno de nota, vimos as configurações de um iniciador de aplicativos, criamos um pipeline de fluxo ETL usando o Spring Cloud Data Flow Shell e implementamos aplicativos personalizados para nossos dados de leitura, transformação e gravação.
Como sempre, o código de exemplo pode ser encontradoin the GitHub project.