A API Flink DataStream oferece um modelo de programação flexível para criar transformações de dados personalizadas, operações e operadores que lidam com lógicas de negócio complexas e processamento de dados.
Compatibilidade com Apache Flink
A API DataStream do Realtime Compute for Apache Flink é totalmente compatível com a versão open source do Apache Flink. Para mais informações, consulte O que é o Apache Flink? e Guia de Programação da API Flink DataStream.
Pré-requisitos
Instale um ambiente de desenvolvimento integrado (IDE), como o IntelliJ IDEA.
Instale o Maven 3.6.3 ou superior.
O desenvolvimento de jobs suporta apenas JDK 8 e JDK 11.
Desenvolva seu job JAR localmente antes de implantá-lo e executá-lo no console do Realtime Compute for Apache Flink.
Antes de começar
Prepare antecipadamente as fontes de dados necessárias, pois este exemplo utiliza conectores de fonte de dados.
Este exemplo utiliza ApsaraMQ for Kafka (2.6.2) e ApsaraDB RDS for MySQL (8,0) como fontes de dados.
Se você tiver fontes de dados autogerenciadas que exijam acesso à rede pública ou entre VPCs, consulte Network connectivity options.
Caso não tenha uma fonte de dados ApsaraMQ for Kafka, adquira e implante uma instância. Para mais informações, consulte Step 2: Purchase and deploy an instance. Ao implantar a instância, certifique-se de que ela esteja na mesma VPC do workspace do Realtime Compute for Apache Flink.
Se não houver uma fonte de dados ApsaraDB RDS for MySQL disponível, adquira uma instância desse serviço. Para mais informações, consulte Step 1: Create an ApsaraDB RDS for MySQL instance and configure a database. Durante a aquisição, verifique se a instância está na mesma região e VPC do workspace do Realtime Compute for Apache Flink.
Desenvolver o job
Configurar dependências do ambiente Flink
Para evitar conflitos de dependência JAR, siga estas diretrizes:
${flink.version}especifica a versão do Flink para execução do job. Essa versão deve corresponder à versão do Flink do mecanismo VVR selecionado na página de implantação do job. Por exemplo, ao selecionar o mecanismovvr-8.0.9-flink-1.17na página de implantação, a versão correspondente do Flink será1.17.2. Para mais informações sobre as versões do mecanismo VVR, consulte How do I check the Flink version of the current job?.Defina o escopo das dependências do Flink como
providedadicionando<scope>provided</scope>. Isso abrange principalmente dependências não relacionadas a conectores no grupoorg.apache.flinkiniciadas porflink-.No código-fonte do Flink, apenas métodos anotados explicitamente com @Public ou @PublicEvolving constituem APIs públicas. O Realtime Compute for Apache Flink garante compatibilidade somente para esses métodos.
Ao utilizar a API DataStream de um conector Flink integrado, use as dependências fornecidas.
A seguir estão as dependências básicas do Flink. Talvez seja necessário adicionar também dependências de log. Para obter a lista completa, consulte o Exemplo de código completo ao final deste tópico.
Dependências e uso de conectores
Para ler e gravar dados com a API DataStream, utilize um conector DataStream para conectar-se ao Realtime Compute for Apache Flink. O Repositório Central Maven disponibiliza conectores VVR DataStream para uso durante o desenvolvimento.
Utilize apenas os conectores listados como compatíveis com a API DataStream em Supported connectors. Não use conectores sem marcação explícita de suporte à API DataStream, pois interfaces e parâmetros podem mudar futuramente.
Use um conector de uma das seguintes maneiras:
(Recomendado) Carregar como dependência adicional
-
No arquivo pom.xml do job, adicione o conector necessário como dependência do projeto com escopo "provided". Para o arquivo completo de dependências, consulte o Exemplo de código completo ao final deste tópico.
Nota${vvr.version}representa a versão do mecanismo de execução do job. Por exemplo, se o job executar no mecanismovvr-8.0.9-flink-1.17, a versão correspondente do Flink será1.17.2. Recomendamos usar o mecanismo mais recente. Para detalhes sobre versões específicas, consulte Engine.Como o pacote JAR do conector é adicionado como dependência adicional, não é necessário incluí-lo no JAR da aplicação. Portanto, declare o escopo como
provided.
<!-- Kafka connector dependency --> <dependency> <groupId>com.alibaba.ververica</groupId> <artifactId>ververica-connector-kafka</artifactId> <version>${vvr.version}</version> <scope>provided</scope> </dependency> <!-- MySQL connector dependency --> <dependency> <groupId>com.alibaba.ververica</groupId> <artifactId>ververica-connector-mysql</artifactId> <version>${vvr.version}</version> <scope>provided</scope> </dependency> -
Para desenvolver novos conectores ou estender a funcionalidade dos existentes, o projeto também deve depender dos pacotes comuns de conectores
flink-connector-baseouververica-connector-common.<!-- Basic dependency for Flink connector public interfaces --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-base</artifactId> <version>${flink.version}</version> </dependency> <!-- Basic dependency for Alibaba Cloud connector public interfaces --> <dependency> <groupId>com.alibaba.ververica</groupId> <artifactId>ververica-connector-common</artifactId> <version>${vvr.version}</version> </dependency> -
Para configurações de conexão DataStream e exemplos de código, consulte a documentação do conector DataStream correspondente.
Para obter uma lista de conectores compatíveis com a API DataStream, consulte Supported connectors.
-
Implante o job e adicione os pacotes JAR dos conectores correspondentes no campo Additional Dependencies. Para mais informações, consulte Deploy a JAR job. Carregue conectores personalizados ou aqueles fornecidos pelo Realtime Compute for Apache Flink. Para baixar conectores, consulte Conectores.
Por exemplo, carregue os pacotes JAR dos conectores
ververica-connector-mysql-1.17-vvr-8.0.4-1.jareververica-connector-kafka-1.17-vvr-8.0.4-1.jar, além do arquivo de configuraçãoconfig.properties.
Empacotar no JAR do job
-
Adicione os conectores necessários como dependências do projeto no arquivo pom.xml do job. O código abaixo exemplifica os conectores Kafka e MySQL.
Nota${vvr.version}indica a versão do mecanismo do ambiente de execução do job. Por exemplo, se o job executar na versão do mecanismovvr-8.0.9-flink-1.17, a versão correspondente do Flink será1.17.2. Recomendamos usar o mecanismo mais recente. Para mais informações, consulte Engines.Ao empacotar conectores no JAR do job como dependências do projeto, mantenha-os no escopo padrão (compile).
<!-- Kafka connector dependency --> <dependency> <groupId>com.alibaba.ververica</groupId> <artifactId>ververica-connector-kafka</artifactId> <version>${vvr.version}</version> </dependency> <!-- MySQL connector dependency --> <dependency> <groupId>com.alibaba.ververica</groupId> <artifactId>ververica-connector-mysql</artifactId> <version>${vvr.version}</version> </dependency> -
Para desenvolver novos conectores ou estender a funcionalidade dos existentes, o projeto também exige o pacote comum de conectores
flink-connector-baseouververica-connector-common.<!-- Basic dependency for Flink connector public interfaces --> <dependency> <groupId>org.apache.flink</groupId> <artifactId>flink-connector-base</artifactId> <version>${flink.version}</version> </dependency> <!-- Basic dependency for Alibaba Cloud connector public interfaces --> <dependency> <groupId>com.alibaba.ververica</groupId> <artifactId>ververica-connector-common</artifactId> <version>${vvr.version}</version> </dependency> -
Para configurações de conexão DataStream e exemplos de código, consulte a documentação do conector DataStream correspondente.
Para obter uma lista de conectores compatíveis com a API DataStream, consulte Supported connectors.
Ler dependências adicionais do OSS
Jobs JAR do Flink não leem arquivos de configuração locais diretamente pelo método main. Em vez disso, carregue o arquivo de configuração no bucket OSS do workspace, adicione-o como dependência adicional durante a implantação e realize a leitura em tempo de execução. A seção a seguir apresenta um exemplo.
-
Crie um arquivo de configuração chamado config.properties para evitar codificar credenciais diretamente no código.
# Kafka bootstrapServers=host1:9092,host2:9092,host3:9092 inputTopic=topic groupId=groupId # MySQL database.url=jdbc:mysql://localhost:3306/my_database database.username=username database.password=password -
No job JAR, utilize código para ler o arquivo config.properties armazenado no bucket OSS.
Método 1: Ler do bucket do workspace
No painel de navegação à esquerda do console do Realtime Compute for Apache Flink, acesse a página Artifacts e carregue o arquivo.
Durante a execução, os arquivos adicionados no campo Additional Dependencies são carregados no diretório /flink/usrlib do pod onde o job executa.
-
O código a seguir exemplifica a leitura desse arquivo de configuração.
Properties properties = new Properties(); Map<String,String> configMap = new HashMap<>(); try (InputStream input = new FileInputStream("/flink/usrlib/config.properties")) { // Load the property file. properties.load(input); // Obtain the property values. configMap.put("bootstrapServers",properties.getProperty("bootstrapServers")) ; configMap.put("inputTopic",properties.getProperty("inputTopic")); configMap.put("groupId",properties.getProperty("groupId")); configMap.put("url",properties.getProperty("database.url")) ; configMap.put("username",properties.getProperty("database.username")); configMap.put("password",properties.getProperty("database.password")); } catch (IOException ex) { ex.printStackTrace(); }
Método 2: Ler de bucket autorizado
Carregue o arquivo de configuração no bucket OSS de destino.
-
Use o OSSClient para ler o arquivo diretamente do OSS. Para mais informações, consulte Streaming download e Manage access credentials. O código a seguir apresenta um exemplo.
OSS ossClient = new OSSClientBuilder().build("Endpoint", "AccessKeyId", "AccessKeySecret"); try (OSSObject ossObject = ossClient.getObject("examplebucket", "exampledir/config.properties"); BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()))) { // read file and process ... } finally { if (ossClient != null) { ossClient.shutdown(); } }
Escrever a lógica de negócio
-
Integre fontes de dados externas aos programas de fluxo de dados do Flink. Uma
Watermarké uma estratégia de computação do Flink baseada em semântica de tempo, frequentemente usada com timestamps. Portanto, este exemplo não utiliza estratégia de watermark. Para mais informações, consulte Estratégia de Watermark.// Integrate the external data source into the Flink data stream program. // WatermarkStrategy.noWatermarks() indicates that no watermark strategy is used. DataStreamSource<String> stream = env.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "kafka Source"); -
Neste exemplo, a transformação por operador converte um
DataStream<String>em umDataStream<Student>. Para transformações de operadores mais complexas e métodos de processamento, consulte Operadores Flink.// Operator that converts the data structure to Student. DataStream<Student> source = stream .map(new MapFunction<String, Student>() { @Override public Student map(String s) throws Exception { // Data is separated by commas. String[] data = s.split(","); return new Student(Integer.parseInt(data[0]), data[1], Integer.parseInt(data[2])); } }).filter(student -> student.score >=60); // Filter for data where the score is 60 or higher.
Empacotar o job
Empacote o job utilizando o maven-shade-plugin.
Se adicionar o conector como dependência adicional, garanta que o escopo das dependências do conector esteja definido como
providedao empacotar o job.Caso empacote conectores no JAR do job, utilize o escopo padrão (compile).
Testar e implantar o job
Por padrão, o Realtime Compute for Apache Flink não acessa a internet pública, impedindo testes diretos em ambiente local. Recomendamos realizar testes unitários separadamente. Para mais informações, consulte Run and debug a job with connectors locally.
-
Para implantar o job JAR, consulte Deploy a JAR job.
NotaDurante a implantação, se utilizar conectores como dependências adicionais, certifique-se de carregar os pacotes JAR relevantes.
Se precisar ler um arquivo de configuração, carregue-o também como dependência adicional.
Exemplo de código completo
Este exemplo processa dados de uma fonte ApsaraMQ for Kafka e grava o resultado em um sink ApsaraDB RDS for MySQL. O conteúdo serve apenas como referência. Para mais diretrizes sobre estilo e qualidade de código, consulte o Guia de estilo e qualidade de código.
Este exemplo omite configurações de parâmetros de execução como checkpoint, TTL e estratégia de reinicialização. Defina essas opções na página de deployment details após a implantação do job. Como as configurações no código têm prioridade maior, recomendamos defini-las após a implantação para simplificar atualizações futuras. Para mais informações, consulte Configure job deployment information.
FlinkDemo.java
package com.aliyun;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
import org.apache.flink.connector.jdbc.JdbcExecutionOptions;
import org.apache.flink.connector.jdbc.JdbcSink;
import org.apache.flink.connector.jdbc.JdbcStatementBuilder;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.connector.kafka.source.enumerator.initializer.OffsetsInitializer;
import org.apache.flink.connector.kafka.source.reader.deserializer.KafkaRecordDeserializationSchema;
import org.apache.flink.kafka.shaded.org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class FlinkDemo {
// Define the data structure.
public static class Student {
public int id;
public String name;
public int score;
public Student(int id, String name, int score) {
this.id = id;
this.name = name;
this.score = score;
}
}
public static void main(String[] args) throws Exception {
// Create a Flink execution environment.
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
Properties properties = new Properties();
Map<String,String> configMap = new HashMap<>();
try (InputStream input = new FileInputStream("/flink/usrlib/config.properties")) {
// Load the property file.
properties.load(input);
// Obtain the property values.
configMap.put("bootstrapServers",properties.getProperty("bootstrapServers")) ;
configMap.put("inputTopic",properties.getProperty("inputTopic"));
configMap.put("groupId",properties.getProperty("groupId"));
configMap.put("url",properties.getProperty("database.url")) ;
configMap.put("username",properties.getProperty("database.username"));
configMap.put("password",properties.getProperty("database.password"));
} catch (IOException ex) {
ex.printStackTrace();
}
// Build Kafka source
KafkaSource<String> kafkaSource = KafkaSource.<String>builder()
.setBootstrapServers(configMap.get("bootstrapServers"))
.setTopics(configMap.get("inputTopic"))
.setStartingOffsets(OffsetsInitializer.latest())
.setGroupId(configMap.get("groupId"))
.setDeserializer(KafkaRecordDeserializationSchema.valueOnly(StringDeserializer.class))
.build();
// Integrate the external data source into the Flink data stream program.
// WatermarkStrategy.noWatermarks() indicates that no watermark strategy is used.
DataStreamSource<String> stream = env.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "kafka Source");
// Filter for scores of 60 or higher.
DataStream<Student> source = stream
.map(new MapFunction<String, Student>() {
@Override
public Student map(String s) throws Exception {
String[] data = s.split(",");
return new Student(Integer.parseInt(data[0]), data[1], Integer.parseInt(data[2]));
}
}).filter(Student -> Student.score >=60);
source.addSink(JdbcSink.sink("INSERT IGNORE INTO student (id, username, score) VALUES (?, ?, ?)",
new JdbcStatementBuilder<Student>() {
public void accept(PreparedStatement ps, Student data) {
try {
ps.setInt(1, data.id);
ps.setString(2, data.name);
ps.setInt(3, data.score);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
},
new JdbcExecutionOptions.Builder()
.withBatchSize(5) // Number of records per batch write.
.withBatchIntervalMs(2000) // Batch interval in milliseconds.
.build(),
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl(configMap.get("url"))
.withDriverName("com.mysql.cj.jdbc.Driver")
.withUsername(configMap.get("username"))
.withPassword(configMap.get("password"))
.build()
)).name("Sink MySQL");
env.execute("Flink Demo");
}
}
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.aliyun</groupId>
<artifactId>FlinkDemo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>FlinkDemo</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<flink.version>1.17.1</flink.version>
<vvr.version>1.17-vvr-8.0.4-1</vvr.version>
<target.java.version>1.8</target.java.version>
<maven.compiler.source>${target.java.version}</maven.compiler.source>
<maven.compiler.target>${target.java.version}</maven.compiler.target>
<log4j.version>2.14.1</log4j.version>
</properties>
<dependencies>
<!-- Apache Flink dependencies -->
<!-- These dependencies are set to 'provided' because they should not be packaged into the JAR file. -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<!-- Add connector dependencies here. They must be in the default scope (compile). -->
<dependency>
<groupId>com.alibaba.ververica</groupId>
<artifactId>ververica-connector-kafka</artifactId>
<version>${vvr.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.ververica</groupId>
<artifactId>ververica-connector-mysql</artifactId>
<version>${vvr.version}</version>
</dependency>
<!-- Add a logging framework to generate console output at runtime. -->
<!-- By default, these dependencies are excluded from the application JAR. -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Java Compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${target.java.version}</source>
<target>${target.java.version}</target>
</configuration>
</plugin>
<!-- We use the maven-shade-plugin to create a fat JAR that contains all necessary dependencies. -->
<!-- Modify the value of <mainClass> if your program's entry point changes. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<!-- Exclude unnecessary dependencies. -->
<configuration>
<artifactSet>
<excludes>
<exclude>org.apache.flink:force-shading</exclude>
<exclude>com.google.code.findbugs:jsr305</exclude>
<exclude>org.slf4j:*</exclude>
<exclude>org.apache.logging.log4j:*</exclude>
</excludes>
</artifactSet>
<filters>
<filter>
<!-- Do not copy the signatures from the META-INF directory.
Otherwise, security exceptions may be thrown when you use the JAR file. -->
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.aliyun.FlinkDemo</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Documentação relacionada
Para obter uma lista de conectores compatíveis com a API DataStream, consulte Supported connectors.
Para um exemplo completo de ponta a ponta do fluxo de trabalho de desenvolvimento de jobs JAR do Flink, consulte Flink JAR jobs.
O Realtime Compute for Apache Flink também suporta jobs SQL e Python. Para orientações de desenvolvimento, consulte Job development overview e Develop Python jobs.