4、Flink入门:Flink架构与核心组件、Flink开发环境搭建(本地模式)、第一个Flink WordCount程序
好,咱们正式进入 Flink 的世界。
这一章,我带你从零搭一套本地开发环境,再亲手跑起来第一个 WordCount 程序。别小看这个 WordCount,它背后藏着 Flink 最核心的架构思想。搞懂了它,后面学窗口、状态、容错都会顺很多。
4.1 Flink 架构与核心组件
先聊聊 Flink 到底长什么样。说白了,Flink 是一个「有状态的、针对无界/有界数据流」的分布式计算引擎。
我刚开始接触 Flink 时,最困惑的是它跟 Spark Streaming 到底有啥区别。后来在做一个实时风控项目时,发现 Flink 的「事件驱动」和「精确一次语义」确实更贴合业务需求。嗯,这里我简单拆解一下它的架构。
Flink 运行时架构(简化版)
一个 Flink 集群由三部分组成:Client、JobManager、TaskManager。数据流从 Source 进入,经过 Transformation,最后从 Sink 出去。
4.2 核心组件详解
咱们把上面图里的几个角色拆开看看。
| 组件 | 职责 | 我的经验 |
|---|---|---|
| Client | 提交作业、触发执行、获取执行结果 | Client 本身不参与计算,提交完就可以退出 |
| JobManager | 作业调度、Checkpoint 协调、故障恢复 | 它是集群的「大脑」,挂了整个集群就瘫了 |
| TaskManager | 执行具体的 Task,管理 Slot 资源 | 每个 TaskManager 可以有多个 Slot,Slot 是资源隔离的最小单位 |
| Slot | TaskManager 内部的资源槽位,每个 Slot 运行一个 SubTask | 我习惯把 Slot 数设成 CPU 核数,避免过度竞争 |
💡 小提示:Flink 的 Slot 是静态划分的,不像 Spark 那样动态申请。所以规划集群时,Slot 总数决定了你的并行度上限。
4.3 开发环境搭建(本地模式)
本地模式最简单,也最适合学习。你不需要搭集群,一个命令就能跑起来。
4.3.1 前置条件
- JDK 8 或 11(我推荐 JDK 11,性能更好)
- Maven 3.6+(用于构建项目)
- IDE(IntelliJ IDEA 最佳,VSCode 也行)
4.3.2 下载与启动
- 去 Flink 官网下载最新稳定版(我写这章时是 1.17.x)
- 解压到本地目录,比如
/opt/flink-1.17.0 - 进入
bin/目录,执行./start-cluster.sh - 打开浏览器访问
http://localhost:8081,看到 Web UI 就说明成功了
⚠️ 注意:本地模式默认使用 8081 端口。如果被占用,可以修改 conf/flink-conf.yaml 里的 rest.port 配置。
4.3.3 项目骨架
我习惯用 Maven 模板创建项目。你也可以直接复制下面的 pom.xml 核心依赖:
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>1.17.0</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java</artifactId>
<version>1.17.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients</artifactId>
<version>1.17.0</version>
</dependency>
</dependencies>
4.4 第一个 Flink WordCount 程序
终于到了动手环节。WordCount 是 Flink 的「Hello World」,但它的代码结构能让你看到 Flink 的完整执行链路。
4.4.1 代码实现
我直接贴一个完整的、可运行的版本。你复制到 IDE 里就能跑。
import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;
public class WordCount {
public static void main(String[] args) throws Exception {
// 1. 创建执行环境
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
// 2. 数据源:从 socket 读取实时流
DataStream<String> text = env.socketTextStream("localhost", 9999);
// 3. 转换逻辑:切分 -> 计数 -> 聚合
DataStream<Tuple2<String, Integer>> counts = text
.flatMap(new Tokenizer())
.keyBy(value -> value.f0)
.sum(1);
// 4. 输出结果
counts.print();
// 5. 启动执行
env.execute("Flink WordCount");
}
public static class Tokenizer implements FlatMapFunction<String, Tuple2<String, Integer>> {
@Override
public void flatMap(String value, Collector<Tuple2<String, Integer>> out) {
String[] words = value.toLowerCase().split("\\W+");
for (String word : words) {
if (word.length() > 0) {
out.collect(new Tuple2<>(word, 1));
}
}
}
}
}
4.4.2 运行步骤
- 在终端启动 netcat:
nc -lk 9999 - 在 IDE 中运行 WordCount 的 main 方法
- 在 netcat 窗口输入一些英文单词,比如
hello world hello flink - 观察 IDE 控制台输出:
(hello,2) (world,1) (flink,1)
🔍 代码拆解:
- StreamExecutionEnvironment:Flink 程序的入口,负责设置执行参数
- socketTextStream:从 socket 读取数据流,这是 Source
- flatMap:将一行文本拆成多个单词,每个单词变成 (word, 1)
- keyBy:按单词分组,类似 SQL 的 GROUP BY
- sum:对第二个字段求和,就是计数
- print:将结果输出到标准输出,这是 Sink
4.4.3 避坑指南
⚠️ 我曾经踩过的坑:
- 端口被占用:netcat 的 9999 端口如果被其他程序占用,Flink 会连不上。用
lsof -i :9999检查一下。 - 并行度问题:本地模式默认并行度是 CPU 核数。如果你用
print()输出,多个线程的输出会交错,看起来乱。可以设置env.setParallelism(1)让输出有序。 - 依赖冲突:Flink 自带的依赖跟项目里的版本不一致时,会报
NoClassDefFoundError。我建议把 flink-streaming-java 的 scope 设为 provided,避免打包时重复。
4.5 理解 Flink 的执行流程
跑通了 WordCount,咱们回头看看 Flink 到底是怎么处理这条数据流的。
你看,整个流程就是一条流水线。数据从 Source 流进来,经过每个算子处理,最后从 Sink 出去。每个算子都可以并行执行,这就是 Flink 高性能的秘密。
💡 我的习惯:刚开始学 Flink 时,我建议你多改改 WordCount 的代码。比如把 socketTextStream 换成 fromElements 读本地数据,或者把 print() 换成 writeAsText 写到文件。改一次,理解深一层。
好了,这一章的内容就到这。你搭好了环境,跑通了第一个程序,也看到了 Flink 的架构全貌。接下来,咱们会深入 Flink 的 DataStream API,看看怎么处理更复杂的实时逻辑。