The Flink DataStream API offers a flexible programming model for creating custom data transformations, operations, and operators to handle complex business logic and data processing.
Apache Flink compatibility
The DataStream API supported by Realtime Compute for Apache Flink is fully compatible with the open source Apache Flink version. For more information, see What is Apache Flink? and Flink DataStream API Programming Guide.
Prerequisites
-
An integrated development environment (IDE), such as IntelliJ IDEA, is installed.
-
Maven 3.6.3 or later is installed.
-
Job development supports only JDK 8 and JDK 11.
-
Develop your JAR job locally before deploying and running it in the Realtime Compute for Apache Flink console.
Before you begin
Prepare the required data sources in advance, as this example uses data source connectors.
-
This example uses ApsaraMQ for Kafka (2.6.2) and ApsaraDB RDS for MySQL (8.0) as data sources.
-
If you have self-managed data sources that require public network access or cross-VPC access, see Network connectivity options.
-
If you do not have an ApsaraMQ for Kafka data source, purchase and deploy an instance. For more information, see Step 2: Purchase and deploy an instance. When you deploy the instance, make sure it is in the same VPC as your Realtime Compute for Apache Flink workspace.
-
If you do not have an ApsaraDB RDS for MySQL data source, purchase an ApsaraDB RDS for MySQL instance. For more information, see Step 1: Create an ApsaraDB RDS for MySQL instance and configure a database. When you purchase the instance, make sure it is in the same region and VPC as your Realtime Compute for Apache Flink workspace.
Develop the job
Configure Maven (optional)
Configure the Maven settings.xml file. If you experience slow speeds or failures when downloading from the Maven central repository, you can switch to the Alibaba Cloud Maven mirror.
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<name>Aliyun Maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
Configure Flink environment dependencies
To avoid JAR dependency conflicts, follow these guidelines:
-
${flink.version}specifies the Flink version for the job execution. This version must be consistent with the Flink version of the VVR engine that you selected on the job deployment page. For example, if you select thevvr-8.0.9-flink-1.17engine on the job deployment page, its corresponding Flink version is1.17.2. For more information about VVR engine versions, see How do I check the Flink version of the current job?. -
For Flink dependencies, set the scope to
providedby adding<scope>provided</scope>. These mainly include non-Connector dependencies in theorg.apache.flinkgroup that start withflink-. -
In the Flink source code, only methods explicitly annotated with @Public or @PublicEvolving are public APIs. Realtime Compute for Apache Flink guarantees compatibility only for these methods.
-
If you use the DataStream API of a built-in Flink connector, use the provided dependencies.
The following are basic Flink dependencies. You may also need to add logging dependencies. For a complete list of dependencies, see the Complete code example at the end of this topic.
Flink dependencies
Connector dependencies and usage
To read and write data with the DataStream API, use a DataStream connector to connect to Realtime Compute for Apache Flink. The Maven Central Repository provides VVR DataStream connectors that you can use during development.
Use only the connectors that are specified to support the DataStream API in Supported connectors. Do not use a connector if it is not explicitly marked for DataStream API support, because its interfaces and parameters might change in the future.
You can use a connector in one of the following ways:
(Recommended) Upload as additional dependency
-
In your job's pom.xml file, add the required connector as a project dependency with the "provided" scope. For the complete dependency file, see the Complete code example at the end of this topic.
Note-
${vvr.version}is the job runtime engine version. For example, if your job runs on thevvr-8.0.9-flink-1.17engine, the corresponding Flink version is1.17.2. We recommend that you use the latest engine. For information about specific versions, see Engine. -
Because the connector's JAR package is added as an additional dependency, it does not need to be bundled in the application JAR. Therefore, you must declare its scope as
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> -
-
If you need to develop new connectors or extend the functionality of existing connectors, your project also needs to depend on the common connector packages
flink-connector-baseorververica-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> -
For DataStream connection configurations and code examples, see the documentation for the corresponding DataStream connector.
For a list of connectors that support the DataStream API, see Supported connectors.
-
Deploy the job and add the corresponding connector JAR packages in the Additional Dependencies field. For more information, see Deploy a JAR job. You can upload your custom connectors or the connectors provided by Realtime Compute for Apache Flink. To download the connectors, see Connectors.
For example, upload the
ververica-connector-mysql-1.17-vvr-8.0.4-1.jarandververica-connector-kafka-1.17-vvr-8.0.4-1.jarconnector JAR packages, and theconfig.propertiesconfiguration file.
Package in job JAR
-
Add the required connectors as project dependencies in your job's pom.xml file. The following code shows an example for the Kafka and MySQL connectors.
Note-
${vvr.version}is the engine version of the job's runtime environment. For example, if your job runs on thevvr-8.0.9-flink-1.17engine version, its corresponding Flink version is1.17.2. We recommend that you use the latest engine. For more information, see Engines. -
If you package connectors into the job JAR as project dependencies, they must be in the default scope (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> -
-
If you need to develop new connectors or extend the functionality of existing connectors, your project also requires the common connector package
flink-connector-baseorververica-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> -
For DataStream connection configurations and code examples, see the documentation for the corresponding DataStream connector.
For a list of connectors that support the DataStream API, see Supported connectors.
Read additional dependencies from OSS
Flink JAR jobs cannot read local configuration files from the main method. Instead, upload the configuration file to your workspace's OSS bucket, add it as an additional dependency during deployment, and read it at runtime. The following section provides an example.
-
Create a configuration file named config.properties to avoid hardcoding credentials in your code.
# 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 -
In your JAR job, use code to read the config.properties file stored in the OSS bucket.
Method 1: Read from workspace bucket
-
In the left-side navigation pane of the Realtime Compute for Apache Flink console, go to the Artifacts page and upload the file.
-
At runtime, files added in the Additional Dependencies field are loaded into the /flink/usrlib directory of the pod where the job runs.
-
The following code provides an example of how to read this configuration file.
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(); }
Method 2: Read from authorized bucket
-
Upload the configuration file to the target OSS bucket.
-
Use OSSClient to read the file directly from OSS. For more information, see Streaming download and Manage access credentials. The following code provides an example.
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(); } }
-
Write the business logic
-
You can integrate external data sources into Flink data stream programs. A
Watermarkis a Flink computation strategy that is based on time semantics and is often used with timestamps. Therefore, this example does not use a watermark strategy. For more information, see Watermark strategy.// 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"); -
Operator transformation converts a
DataStream<String>to aDataStream<Student>in this example. For more complex operator transformations and processing methods, see Flink Operators.// 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.
Package the job
Package the job by using the maven-shade-plugin.
-
If you add the connector as an additional dependency, ensure that the scope of the connector dependencies is set to
providedwhen you package the job. -
If you package connectors into the job JAR, use the default scope (compile).
Test and deploy the job
-
By default, Realtime Compute for Apache Flink cannot access the public internet, which prevents direct testing in a local environment. We recommend that you perform unit tests separately. For more information, see Run and debug a job with connectors locally.
-
To deploy the JAR job, see Deploy a JAR job.
Note-
During deployment, if you use connectors as additional dependencies, ensure you upload the relevant JAR packages.
-
If you need to read a configuration file, you must also upload it as an additional dependency.
-
Complete code example
This example processes data from an ApsaraMQ for Kafka source and writes the result to an ApsaraDB RDS for MySQL sink. This example is for reference only. For more code style and quality guidelines, see Code style and quality guide.
This example omits configurations for runtime parameters such as checkpoint, TTL, and restart strategy. You can configure these settings on the deployment details page after the job is deployed. Because settings in code have higher priority, we recommend configuring them after deployment to simplify future updates. For more information, see 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>
Related documentation
-
For a list of connectors that support the DataStream API, see Supported connectors.
-
For a complete, end-to-end example of the Flink JAR job development workflow, see Flink JAR jobs.
-
Realtime Compute for Apache Flink also supports SQL and Python jobs. For development guidance, see Job development overview and Develop Python jobs.