一、发行版解剖:/lib 与 /opt 各管啥?
(1)核心运行时(flink-dist.jar)
- 位于
/lib
,包含协调、网络、Checkpoint、故障恢复、API、算子(窗口)、资源管理等系统核心。 - 类比 Java 的
String
/List
:启动任何 Flink 应用都离不开它。
(2)/lib 目录:默认加载
- 附带常用模块:Table 作业所需的 API/Runtime/Planner Loader ,以及一组常用连接器与格式。
- 想禁用?从
/lib
移走对应 JAR 即可从类路径排除。
(3)/opt 目录:可选依赖
- 放的是可选 JAR ,需要时拷贝到
/lib
启用(如 Scala 版 Planner)。
(4)为什么核心不带连接器/库?
- 控制体积与避免冲突 。连接器/CEP/SQL/ML 等非核心库不默认打入核心依赖。
实践建议
- 做"最小可用类路径":只保留作业必需的 JAR,降低冷启动与冲突概率。
- 环境差异定位时,先对比
/lib
与/opt
变更记录。
二、Scala 版本:二进制不兼容的刚性约束
(1)不同行不兼容
- Flink 的 Scala 构件名带后缀 (如
..._2.12
),必须与应用 Scala 版本一致。 - 只用 Java API 可不关心 Scala 版本。
(2)2.12.8 之后的断裂
- Scala 2.12.8 之后 与更早 2.12.x 不二进制兼容,Flink 官方构建停在 2.12.8。
- 如需更高版本:本地构建 Flink ,并在构建时加入
-Djapicmp.skip
跳过兼容性检查。
(3)选型准则
- 能用 Java API 就用 Java API;
- 确需 Scala API:锁定 Scala 版本,统一团队与集群环境;避免多版本并存。
三、Table 依赖拆箱:API / Runtime / Planner Loader
(1)默认自带的三件套(在 /lib
)
flink-table-api-java-uber-2.1.0.jar
:Java Table APIflink-table-runtime-2.1.0.jar
:运行时flink-table-planner-loader-2.1.0.jar
:Planner Loader(隔离类路径)
早期是一个
flink-table.jar
,从 1.15 起拆分,便于替换 Planner。
(2)Scala API 不默认打包
-
若用 Scala Table API 的格式/连接器:
- 推荐 手动把对应 JAR 放入发行版
/lib
,或 - 打进作业的 uber/fat JAR。
- 推荐 手动把对应 JAR 放入发行版
(3)连接外部系统
- Table/SQL 常用SQL Connector(uber 版)体验更顺滑;DataStream 更偏向 thin+shade 精细控依赖。
四、Planner vs Planner Loader:只用一个!
(1)两种 Planner 的区别
/opt
:flink-table-planner_2.12-2.1.0.jar
(直接 Planner ,暴露 内部包;需匹配 Scala 2.12)/lib
:flink-table-planner-loader-2.1.0.jar
(Loader 包装 ,Planner 隐藏在隔离类路径里;不需考虑 Scala)
(2)默认使用 Loader
- 普通应用无需关心 Scala,更稳定、升级摩擦小。
- 只有在必须访问 Planner 内部实现 时,才把
/opt
的planner_2.12
换到/lib
。
(3)严禁共存
- 两者不可同时在类路径 (同时放
/lib
),否则Table 作业启动失败。 - 未来版本可能不再提供 Scala 直出的 Planner → 建议迁移到 API 层,不要耦合 Planner 内部。
五、Hadoop 依赖:放到"系统",别放到"应用"
(1)通用规则
- 不要 把 Hadoop 作为应用依赖打进作业 JAR。
- 要用 Hadoop (如 HDFS、YARN、Kerberos),请使用包含 Hadoop 的 Flink 运行环境。
(2)使用环境变量注入
bash
export HADOOP_CLASSPATH=`hadoop classpath`
- Flink 会读取
HADOOP_CLASSPATH
来使用 Hadoop 依赖。
(3)设计原因
- 部分 Hadoop 交互在用户代码启动前就发生:HDFS Checkpoint、Kerberos 认证、YARN 部署。
- 反向类加载(inverted classloading) :把大树状传递依赖与核心隔离,允许应用与系统不同版本共存而不冲突。
(4)在 IDE 开发/测试
- 需要 HDFS?将 Hadoop 依赖按
provided
/test
作用域 配置即可,不要打进最终产物。
六、工程落地清单(可直接用)
(1)类路径治理
- 仅保留必要 JAR:最小化
/lib
;可选 JAR 放/opt
,需要时再移入/lib
。 - 做一次"类路径快照"纳入 CM,定位环境问题就靠它。
(2)Table 作业规范
- 默认用 Planner Loader ;禁止和 Scala Planner 共存。
- Scala Table API 的格式/连接器:统一放
/lib
或 shade 进 uber JAR,避免混搭。
(3)Scala 策略
- Java API 优先;Scala API 统一版本(推荐 2.12.8)。
- 如需更高版本 Scala,自建构建链并锁定产物。
(4)Hadoop 策略
- 在平台侧 提供 Hadoop 依赖,不要进应用 JAR。
- 统一
HADOOP_CLASSPATH
注入方式;敏感认证(如 Kerberos)走运维管控。
(5)依赖冲突防线
- Maven:
mvn dependency:tree
;Gradle:gradle dependencies
。 - 冲突常见源:把
flink-table-runtime
/planner-loader
/flink-clients
误打进作业 JAR。
七、常见坑与快速排查
(1)Table 作业启动即挂
- 排查
/lib
是否同时存在planner-loader
与planner_2.12
。 - 清理后只保留其一再启动。
(2)SQL Client 找不到 Connector
- 确认对应 sql-connector(uber)JAR 已放入
/lib
或已 shade 进作业包。 - 不要混入 thin 版却缺少第三方依赖。
(3)类冲突 / NoSuchMethodError
- 通常是把 Flink 运行时 打进了作业 uber JAR(
flink-clients
、flink-table-runtime
、planner-loader
)。 - 调整为
provided
/compileOnly
,重打包。
(4)Scala 版本不匹配
- 使用 Scala Planner 时,一定要与应用 Scala 版本一致 ;否则直接换成 Planner Loader 避坑。
(5)HDFS 访问失败
- 检查
HADOOP_CLASSPATH
注入是否正确、集群节点是否一致; - Kerberos 票据是否有效、时间同步是否正常。
八、参考模板(两段即用)
(1)Maven:排除把运行时打入 uber JAR 的误操作
xml
<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-clients</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>
<!-- 连接器 & 格式用 compile,并用 Shade 打进 uber JAR -->
(2)Gradle Shadow:Manifest 写主类 + 清理签名
groovy
tasks.shadowJar {
archiveClassifier.set('all')
mergeServiceFiles()
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA'
manifest { attributes 'Main-Class': 'org.example.MyJob' }
}
九、结语
- 发行版结构决定了你如何"减法"做类路径治理;
- Scala 兼容性决定了你能否无痛升级与复用生态;
- Table 三件套与 Planner 选择决定了稳定性与可维护性;
- Hadoop 依赖在系统侧是避免冲突与提前初始化的关键。
把这些"地基工程"打牢,你的 Flink 集群将更稳 、作业更易演进 、定位问题也更快 。在真正的生产体系里,配置即架构------做好配置,就是在给未来的扩展与排障省时间。