文章目录
Flink 概述
Apache Flink 是一个开源的流处理框架,专为分布式、高性能、随时可用且准确的流处理应用程序而设计。它提供了丰富的状态管理功能和事件时间支持,能够确保精确一次的计算语义,非常适合构建可扩展的ETL、数据分析和事件驱动的流式应用程序。
Flink 能够处理无界和有界数据流,并且能够运行在各种集群资源管理器上,如 YARN、Mesos 和 Kubernetes,同时也支持裸机集群部署。
有界数据集指的是数据量是有限的、已知的,并且可以在某个时间点完全获取或处理的数据集合。
无界数据流指的是数据是连续不断生成的,数据量理论上是无限的,或者在处理时数据尚未完全产生。
Flink 特点
-
高吞吐和低延迟
Flink 设计用于实现高速数据流处理,它可以在毫秒级别实现低延迟。 -
有状态的流处理
Flink 支持有状态的流处理操作,允许用户在无界数据流上维护和访问状态。 -
事件时间(Event Time)语义
Flink 支持基于事件时间的流处理,允许程序正确处理乱序事件和处理时间的不确定性。 -
水印(Watermarks)机制
Flink 使用水印机制来处理事件时间以及乱序事件,确保事件能够按照时间顺序进行处理。 -
窗口操作
Flink 提供了丰富的窗口操作,包括滚动窗口、滑动窗口和会话窗口等。 -
精确一次(Exactly-once)状态一致性
Flink 通过其检查点(Checkpoint)机制和状态后端确保了即使在故障发生后,也能提供精确一次的状态一致性。 -
多样化的 API
Flink 提供了 DataStream API 用于流处理,DataSet API 用于批处理,Table API 和 SQL 用于关系型数据操作。
Flink 应用场景
Flink 的应用场景非常广泛,包括但不限于:
-
事件驱动型应用:例如实时推荐、金融反欺诈、实时规则预警等,这类应用通常根据事件流中的事件触发计算、更新状态或进行外部系统操作。
-
数据分析型应用:例如营销大屏、销量统计、用户行为分析等,这些应用需要对大量数据进行实时分析和聚合。
-
数据管道型应用(ETL):Flink 通过丰富的 Connector 支持多种数据源和数据 Sink,非常适合进行数据的抽取、转换和加载。
Flink 与 Spark Streaming 对比
在我们学习 Flink 的时候,常常会和 Spark Streaming 进行比较,那么,通过下面的表格,概述了两者在不同特性上的差异:
对比项 | Flink | Spark Streaming |
---|---|---|
处理模型 | 原生流处理模型(Native) | 微批处理(Micro-Batching) |
API 类型 | 声明式 | 声明式 |
语义保证 | 精确一次(Exactly-once) | 精确一次(Exactly-once) |
容错机制 | 检查点(Checkpoint) | 检查点(Checkpoint) |
状态管理 | 有状态 | 基于 DStream |
数据延迟 | 低 | 中等 |
吞吐量 | 高 | 高 |
编程范式 | 支持有状态的流处理和批处理 | 主要面向批处理,流处理是扩展 |
窗口操作 | 支持时间、计数、会话等多种窗口操作 | 支持滑动窗口、滚动窗口 |
状态后端 | 支持多种状态后端,如内存、文件系统、RocksDB | 通常依赖于外部存储系统 |
容错与恢复 | 支持端到端的精确一次语义 | 支持但需要外部系统的配合 |
内存管理 | 优化的内存管理,支持 Off-Heap 内存 | 通常使用 JVM 堆内存 |
易用性 | 易于进行状态管理和时间操作 | 易于与 Spark 生态系统集成 |
社区和生态 | 活跃的开源社区 | 强大的社区和成熟的生态系统 |
批处理与流处理环境
在 Flink 中,主要有批处理 ExecutionEnvironment
和 流处理 StreamExecutionEnvironment
两种不同的执行环境,它们分别用于批处理和流处理操作。
ExecutionEnvironment
- 批处理
ExecutionEnvironment
是 Flink 中用于执行批处理操作的环境。它提供了创建数据集(DataSet)API 的方法,允许用户对静态数据集进行操作。 - 特性
- 用于有界数据集的处理。
- 操作通常在数据已经全部加载到内存中后执行。
- 适合于传统的批处理作业。
StreamExecutionEnvironment
- 流处理
StreamExecutionEnvironment
是 Flink 中用于创建流处理作业的环境。它提供了创建数据流(DataStream)API 的方法,允许用户对无界或有界数据流进行操作。 - 特性
- 用于无界数据流的处理,但同时也支持有界数据流。
- 可以持续地从数据源接收数据,并进行实时处理。
- 适合于实时分析和事件驱动的应用程序。
区别
-
数据模型:
ExecutionEnvironment
针对批处理,操作在 DataSet 上;而StreamExecutionEnvironment
针对流处理,操作在 DataStream 上。 -
应用场景:批处理环境用于处理有限的数据集,通常在数据已经全部可用时;流处理环境用于处理连续的数据流,可以是无限的,通常需要实时处理。
-
API:两者提供的 API 不同,
StreamExecutionEnvironment
提供了如时间窗口、watermarks 等流特定的操作。 -
执行方式:批处理作业通常在所有数据加载完成后执行,而流处理作业则是持续不断地处理实时到达的数据。
如果你正在编写一个需要实时处理数据的应用程序,那么 StreamExecutionEnvironment
是更合适的选择。相反,如果你需要处理存储在文件系统或数据库中的静态数据集,那么 ExecutionEnvironment
将更加适合。
WordCount
下面的示例均采用 Java 进行演示,首先构建 Maven 项目,并引入依赖文件,参考如下:
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.jsu</groupId>
<artifactId>flink</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<flink.version>1.17.0</flink.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients</artifactId>
<version>${flink.version}</version>
</dependency>
</dependencies>
</project>
有界词频统计
读取一个静态的文本文件 words.txt
,进行词频统计,如下所示:
hello flink
hello bigdata
hello man
hello woman
因为读取的是一个静态的数据集,所以我们使用 ExecutionEnvironment
环境来进行处理。
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.operators.Order;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.AggregateOperator;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.operators.FlatMapOperator;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.util.Collector;
public class WordCount {
public static void main(String[] args) throws Exception {
// 1.创建环境
ExecutionEnvironment environment = ExecutionEnvironment.getExecutionEnvironment();
// 2.读取数据
DataSource<String> dataSource = environment.readTextFile("src/main/resources/words.txt");
// 3.转换数据格式
FlatMapOperator<String, Tuple2<String, Long>> flatMap = dataSource.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Long>> collector) {
String[] wordList = line.split(" ");
for (String word : wordList) {
collector.collect(Tuple2.of(word, 1L));
}
}
});
// 4.分组统计个数并输出结果
flatMap.groupBy(0).sum(1).print();
}
}
输出结果如下:
![](https://i-blog.csdnimg.cn/direct/2d3b745d7be54cc0b62001b0a9ba59b2.png)
无界词频统计
这里我创建了一台虚拟机,并通过 nc
服务创建了一个网络监听连接,用于传输数据,如下所示:
![](https://i-blog.csdnimg.cn/direct/799fd5e8616c414cb7740f7f896c2767.png)
nc
,即 Netcat,是一个用于网络工具,可以用于读写网络连接,使用 TCP 或 UDP 协议。执行nc -l 7777
命令后,Netcat 将开始在本机的7777
端口上监听传入的连接请求。如果有远程主机连接到这个端口,Netcat 将与之建立连接,并可以进行数据传输。
创建 Socket 流读取程序,进行词频统计。
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
public class WordCountSocket {
public static void main(String[] args) throws Exception {
// 1.创建流式处理环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 2.从socket中读取数据
DataStreamSource<String> streamSource = env.socketTextStream("hadoop201", 7777);
// 3.转换数据格式
SingleOutputStreamOperator<Tuple2<String, Long>> streamOperator = streamSource.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Long>> collector) throws Exception {
String[] split = line.split(" ");
for (String s : split) {
collector.collect(Tuple2.of(s, 1L));
}
}
});
// 4.分组统计个数并输出结果
streamOperator.keyBy(data -> data.f0).sum(1).print();
// 5.启动环境
env.execute();
}
}
输出示例如下:
因为是流式处理的原因,所以形成了这种累加的效果,会依次对每一行数据发送过来的数据进行处理,等待处理完成后,再读取新的数据。
注意,启动程序前,需要先启动 nc
监听服务,否则程序会抛连接异常。
如果咱们没有虚拟机,那么也可以通过流式的方式读取上面的静态文本文件进行处理,效果是一样的,如下所示:
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
public class WordCountStream {
public static void main(String[] args) throws Exception {
// 1.创建流式处理环境
StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();
// 2.读取静态文本数据
DataStreamSource<String> dataStream = environment.readTextFile("src/main/resources/words.txt");
// 3.转换数据格式
SingleOutputStreamOperator<Tuple2<String, Long>> flatMap = dataStream.flatMap(new FlatMapFunction<String, Tuple2<String, Long>>() {
@Override
public void flatMap(String line, Collector<Tuple2<String, Long>> collector) {
String[] wordList = line.split(" ");
for (String word : wordList) {
collector.collect(Tuple2.of(word, 1L));
}
}
});
// 4.分组统计个数并输出
flatMap.keyBy(data -> data.f0).sum(1).print();
// 5.启动环境
environment.execute();
}
}
输出结果如下:
虽然和批处理读取的内容是一样的,但是输出结果大有不同,流式处理注重的是对每一条数据的处理,而批处理则注重批量数据的处理,相信通过这个案例,能让你对流式处理和批量处理有一个不错的理解。