一、快速起步:用官方骨架拉起项目
Maven:Archetype 或 quickstart 脚本(二选一)
bash
# 方式一:Archetype(交互式填写 groupId/artifactId/package)
mvn archetype:generate \
-DarchetypeGroupId=org.apache.flink \
-DarchetypeArtifactId=flink-quickstart-java \
-DarchetypeVersion=2.1.0
# 方式二:一行脚本
curl https://flink.apache.org/q/quickstart.sh | bash -s 2.1.0
上述命令会生成一套最小可运行骨架;之后你只需要补齐依赖与打包配置。
Gradle
Gradle 没有官方一键脚本也没关系,直接新建工程并按下文添加依赖与 shadow 配置即可。
二、到底需要哪些依赖?
一个可用的 Flink 作业通常包含 3 类依赖:
- Flink APIs(编写作业逻辑必需)
- 连接器 / 格式(接入 Kafka/Cassandra/文件/JSON/Avro 等)
- 测试工具(本地/集成测试)
此外,你还会按需添加三方库(比如 Jackson、Guava、业务 SDK),用于 UDF/序列化/自定义逻辑。
2.1 Flink API 依赖对照表(常用)
你要用的 API | 需要添加 |
---|---|
DataStream | flink-streaming-java |
Table API(Java) | flink-table-api-java |
Table API(Scala) | flink-table-api-scala_2.12 |
Table + DataStream(Java 桥) | flink-table-api-java-bridge |
Table + DataStream(Scala 桥) | flink-table-api-scala-bridge_2.12 |
混用场景(既要 Table 也要 DataStream)记得加 bridge。
2.2 运行所需的"客户端/运行时"模块
- 直接本地跑 main :需要
flink-clients
在 classpath 中。 - Table 程序 :还需要
flink-table-runtime
与flink-table-planner-loader
。
注意:这些模块通常由 Flink 集群提供 。打包 uber JAR 时不要把它们塞进去(后文有配置)。
三、Maven 版:依赖与打包最佳实践
3.1 pom.xml
依赖示例(精简版)
xml
<properties>
<java.version>17</java.version>
<flink.version>2.1.0</flink.version>
<scala.binary.version>2.12</scala.binary.version>
</properties>
<dependencies>
<!-- APIs(按需选择) -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-streaming-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-java</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-api-java-bridge</artifactId>
<version>${flink.version}</version>
</dependency>
<!-- 连接器/格式(示例:Kafka + JSON) -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-connector-kafka</artifactId>
<version>${flink.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-json</artifactId>
<version>${flink.version}</version>
</dependency>
<!-- 测试工具(按需) -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-test-utils-junit</artifactId>
<version>${flink.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<!-- 本地运行 main 需要(集群环境下建议 provided) -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-clients</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<!-- Table 程序运行时(集群提供,uber JAR 不要打入) -->
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-runtime</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-table-planner-loader</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
3.2 Maven Shade:只打"你必须带的"
目标 :把你的应用代码 + 连接器/格式 + 三方库 打进 uber JAR;不要把集群已提供的 Flink 运行时重复打包(避免冲突与超大 JAR)。
xml
<build>
<plugins>
<!-- Java 版本 & 编译插件略 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<filters>
<!-- 忽略签名元数据:防止合并冲突 -->
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<!-- 避免把 flink-clients/table-runtime/planner-loader 等 provided 进来 -->
<artifactSet>
<excludes>
<exclude>org.apache.flink:flink-clients</exclude>
<exclude>org.apache.flink:flink-table-runtime</exclude>
<exclude>org.apache.flink:flink-table-planner-loader</exclude>
</excludes>
</artifactSet>
<!-- 可选:合并 SPI(例如 Kafka 序列化 SPI) -->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.YourMainClass</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
打包完成后得到一个 可提交的 job JAR:既包含连接器与三方依赖,又不会和集群里的 Flink 运行时"打架"。
四、Gradle 版:依赖与 Shadow 打包
4.1 build.gradle
(Groovy)示例
groovy
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
ext {
flinkVersion = '2.1.0'
scalaBinary = '2.12'
}
repositories { mavenCentral() }
dependencies {
// APIs
implementation "org.apache.flink:flink-streaming-java:${flinkVersion}"
implementation "org.apache.flink:flink-table-api-java:${flinkVersion}"
implementation "org.apache.flink:flink-table-api-java-bridge:${flinkVersion}"
// 连接器/格式
implementation "org.apache.flink:flink-connector-kafka:${flinkVersion}"
implementation "org.apache.flink:flink-json:${flinkVersion}"
// 测试
testImplementation "org.apache.flink:flink-test-utils-junit:${flinkVersion}"
testImplementation "org.junit.jupiter:junit-jupiter:5.10.2"
// 集群提供的运行时(标记为 compileOnly / runtimeOnly)
compileOnly "org.apache.flink:flink-clients:${flinkVersion}"
compileOnly "org.apache.flink:flink-table-runtime:${flinkVersion}"
compileOnly "org.apache.flink:flink-table-planner-loader:${flinkVersion}"
}
tasks.withType(Test).configureEach {
useJUnitPlatform()
}
jar {
manifest {
attributes 'Main-Class': 'com.example.YourMainClass'
}
}
tasks.shadowJar {
archiveClassifier.set('all')
// 典型冲突处理
mergeServiceFiles()
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA'
}
产物为
your-artifact-all.jar
(即 uber JAR),提交到集群即可运行。
五、运行与提交:本地调试 vs 集群提交
本地调试
- 直接运行
main
(IDE/CLI),确保flink-clients
在 classpath。 - Table 程序本地跑需
flink-table-runtime
和flink-table-planner-loader
。
集群提交(Session / Application)
bash
# 提交到已运行的 Session Cluster
flink run -c com.example.YourMainClass /path/to/your-app-all.jar
# Kubernetes Application 模式(示意)
flink run-application \
-t kubernetes-application \
-Dkubernetes.cluster-id=my-flink-app \
-Dtaskmanager.numberOfTaskSlots=2 \
local:///opt/flink/usrlib/your-app-all.jar
六、测试与本地验证(建议)
- 单元测试 :
flink-test-utils-junit
+ JUnit 5,对 UDF/函数逻辑做纯逻辑测试。 - 集成测试 :本地启动 mini-cluster(
MiniClusterWithClientResource
)或 Testcontainers + Kafka,验证端到端拓扑。 - Schema/格式测试:给 JSON/Avro/CSV 读写器构造"好/坏样例",避免线上解析炸裂。
七、上线自检清单(Do/Don't)
Do
- 用 uber JAR 打包你的代码、连接器与三方库。
- 将
flink-clients
/flink-table-runtime
/flink-table-planner-loader
设为 provided/compileOnly(由集群提供)。 - Table + DataStream 混用时记得添加 bridge 依赖。
- 统一 Flink 版本 与 连接器版本(以 Flink 为准),避免 classpath 冲突。
- 为有状态算子设置 稳定的 UID,保障 savepoint 恢复。
Don't
- 不要把 Flink 发行版里的核心运行时打进 uber JAR(体积暴涨 + 冲突)。
- 不要混用不兼容的 Scala 二进制版本 (
_2.11/_2.12
不能乱配)。 - 不要在多个模块里各自 shade 同一大依赖(尽量在最上层汇总)。
八、常见坑与快速排查
- 提交时报类冲突 / NoSuchMethodError
→ 检查是否把flink-table-runtime/planner-loader
打进了 uber JAR;核对依赖树(mvn dependency:tree
/gradle dependencies
)。 - Table 程序本地能跑,集群失败
→ 集群端是否有匹配版本的table-runtime/planner-loader
;Planner Loader 未对得上。 - Kafka 连接器运行时报找不到类
→ 没有把flink-connector-kafka
打进 uber JAR;或版本与 Flink 不匹配。 - Scala 相关报错
→ 统一_2.12
,避免把_2.11
依赖带进来。 - JAR 超大/冷启动慢
→ 排查是否误打入 Flink 自带模块;尽量移除冗余依赖与签名元数据。