第2章:Flink基础与环境搭建——架构、部署与开发环境配置
说实话,很多初学者一上来就急着写代码,结果连Flink怎么跑起来的都没搞明白。我个人习惯是,先花点时间把架构和部署环境摸透,后面写代码才不慌。这一章,我们就从Flink的架构设计讲起,再到集群部署的两种主流方式,最后把开发环境配好。
2.1 Flink架构:核心组件与数据流
Flink的架构,说白了就是一套「主从分工」的体系。我刚开始接触时,总觉得它跟Spark差不多,但用久了才发现,Flink在流处理上的设计更「原生」。
核心组件一览:
- JobManager:集群的「大脑」,负责调度、协调、故障恢复。
- TaskManager:真正干活的「工人」,执行具体的计算任务。
- Dispatcher:提供REST接口,接收作业提交。
- ResourceManager:管理资源分配,对接YARN、Kubernetes等。
你想想看,一个作业提交后,JobManager先把它解析成执行图,然后分发给TaskManager。每个TaskManager里跑着多个Task Slot,这就是并行度的基本单位。
小提示:Task Slot的数量决定了TaskManager能并行执行的子任务数。我一般建议,Slot数不要超过CPU核心数,否则线程切换开销会吃掉性能。
2.2 Flink集群部署:Standalone模式
Standalone模式,就是Flink自己管自己,不依赖外部资源调度器。适合开发测试,或者小规模集群。我在项目初期做原型验证时,就喜欢用这个模式,省心。
部署步骤:
- 下载Flink安装包,解压到所有节点。
- 配置
conf/flink-conf.yaml,设置JobManager地址。 - 配置
conf/workers,列出所有TaskManager节点。 - 在JobManager节点执行
bin/start-cluster.sh。
核心配置项,我整理了一张表,方便你对照着配:
| 配置项 | 默认值 | 说明 |
|---|---|---|
jobmanager.rpc.address |
localhost | JobManager的RPC地址 |
jobmanager.rpc.port |
6123 | RPC通信端口 |
taskmanager.numberOfTaskSlots |
1 | 每个TaskManager的Slot数 |
parallelism.default |
1 | 作业默认并行度 |
注意:Standalone模式下,如果TaskManager挂了,JobManager不会自动重启它。我曾经在生产环境吃过这个亏,后来加了监控脚本才放心。
2.3 Flink集群部署:YARN模式
YARN模式才是生产环境的主流。它把资源管理交给Hadoop YARN,Flink只负责计算。说白了,就是「借鸡生蛋」——YARN帮你管机器,Flink专心跑任务。
YARN模式下有两种提交方式:
- Session模式:先启动一个长集群,然后往里面提交作业。适合短作业、交互式查询。
- Per-Job模式:每个作业启动一个独立集群,作业结束集群自动销毁。适合长作业、资源隔离要求高的场景。
我记得有一次,团队里有人用Session模式跑多个作业,结果一个作业的内存泄漏把整个集群拖垮了。从那以后,我们规定:生产环境必须用Per-Job模式。
YARN模式部署要点:
- 确保Hadoop集群已启动,
YARN和HDFS都可用。 - 设置环境变量
HADOOP_CLASSPATH,Flink需要用它找到YARN的配置。 - 使用
bin/yarn-session.sh启动Session集群,或直接用bin/flink run -m yarn-cluster提交Per-Job作业。
关键参数对比:
| 参数 | Session模式 | Per-Job模式 |
|---|---|---|
| 资源分配 | 固定资源池 | 按作业动态分配 |
| 启动速度 | 快(集群已存在) | 慢(需申请资源) |
| 资源隔离 | 弱(作业间共享) | 强(独立Container) |
| 适用场景 | 开发测试、短作业 | 生产环境、长作业 |
2.4 Flink开发环境配置
环境配好了,接下来就是搭开发环境。我个人习惯用IntelliJ IDEA + Maven,当然Gradle也行,看个人喜好。
步骤一:创建Maven项目
在pom.xml里引入Flink依赖。注意版本号要跟集群一致,否则跑起来会报序列化异常——我踩过这个坑。
<properties>
<flink.version>1.17.0</flink.version>
<scala.binary.version>2.12</scala.binary.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-java</artifactId>
<version>${flink.version}</version>
</dependency>
<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>
步骤二:配置日志框架
Flink默认用Log4j 2,但很多项目用的是Logback。我建议统一用Log4j 2,省得冲突。在 src/main/resources 下放一个 log4j2.properties:
rootLogger.level = INFO
rootLogger.appenderRef.console.ref = ConsoleAppender
appender.console.name = ConsoleAppender
appender.console.type = CONSOLE
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
步骤三:编写第一个Flink程序
写一个最简单的WordCount,验证环境是否通。嗯,这里要注意,Flink的DataSet API在1.12以后已经标记为废弃,新项目一律用DataStream API。
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
public class WordCount {
public static void main(String[] args) throws Exception {
StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.socketTextStream("localhost", 9999)
.flatMap((String line, Collector<String> out) -> {
for (String word : line.split(" ")) {
out.collect(word);
}
})
.keyBy(word -> word)
.sum(1)
.print();
env.execute("Socket WordCount");
}
}
调试技巧:本地开发时,可以用 env.setParallelism(1) 限制并行度,方便在IDE里看日志。我每次调试都这么干,等逻辑没问题了再放开并行度。
2.5 环境验证与常见问题
环境配好后,怎么验证?我的做法是三步走:
- 启动Flink集群,访问Web UI(默认端口8081),确认JobManager和TaskManager都活着。
- 提交一个简单的作业,比如上面的WordCount,看能不能正常跑完。
- 检查TaskManager的日志,确认没有报错。
如果遇到问题,别慌。我列几个常见坑:
- 端口冲突:8081被占了?改
rest.port配置。 - 类加载冲突:依赖版本不一致,尤其是Hadoop和Flink的guava版本。用
mvn dependency:tree排查。 - 内存不足:YARN模式下,Container内存配小了。调大
taskmanager.memory.process.size。
避坑指南:我曾经在YARN集群上部署Flink,结果作业一直卡在「RUNNING」状态,日志里啥也没有。后来发现是Hadoop的 yarn-site.xml 里 yarn.nodemanager.vmem-check-enabled 没关,虚拟内存超限被杀了。嗯,生产环境建议把这个参数设为false。
好了,这一章的内容就到这里。架构、部署、开发环境,这三样东西是Flink的「地基」。地基打牢了,后面写实时特征计算的代码才稳当。