Flink on YARN 依赖/JAR 包问题排查指南

适用问题:ClassNotFoundException、NoClassDefFoundError、NoSuchMethodError、ClassCastException、LinkageError、不同环境(本地/集群)表现不一致、在 TaskManager 上失败等。

Flink 作业运行时可能同时存在多套依赖来源:

  1. Flink 发行包自带 lib

位于 Flink 集群分发目录的 lib/(YARN 模式下会被分发到容器里)。

  1. yarn.provided.lib.dirs(父加载器可见的额外 lib)

通常用于放"大依赖/多作业共用依赖",作用类似集群级共享库。

  1. 作业 Jar(用户代码 Jar)

通过 flink run 提交的 jar;如果是 fat/uber jar,里面还携带第三方依赖。

  1. 插件(plugins/)

一般用于 connector/format 的插件化加载(不同版本隔离)。

核心点:Flink 默认 child-first(优先作业 jar),个别包会被强制 parent-first(Flink 自己的、日

志、部分 Java/Scala 等),并可通过 classloader.parent-first-patterns.additional 调整。

-D classloader.resolve-order=child-first(默认是child-first,不建议改为 parent-first)

-D classloader.parent-first-patterns.additional = okhttp3.,okio.,kotlin.

二、 先按异常类型分流(最快定位方向)

A. ClassNotFoundException / NoClassDefFoundError

含义:运行时找不到类。优先怀疑:

  • 依赖没带上(作业 jar 非 fat、provided 目录缺包)
  • 依赖拆分成多个 jar(例如 Kotlin/OkHttp/Okio),你只放了其中一部分
  • 提交命令参数(-C/-yD)或部署目录不一致,导致容器里没有对应 jar

结论倾向:缺依赖/类不可见。

B. NoSuchMethodError / IncompatibleClassChangeError

含义:类找到了,但版本/二进制签名不匹配。优先怀疑:

  • 同一个库出现两份不同版本(作业 jar + provided / Flink lib)
  • 依赖传递导致版本漂移(A 依赖 okhttp 4.9.3,B 依赖 okhttp 3.x)
  • shaded 与非 shaded 混用

结论倾向:版本冲突。

C. ClassCastException(尤其报"cannot be cast to ..."且类名相同)

含义:同名类被不同 ClassLoader 加载,两份"同类"互相不能 cast。优先怀疑:

  • 同一个 jar 在 parent(provided/lib)和 child(作业 jar)各一份 或 connector/plugin 与作业 jar 重复

结论倾向:类加载器隔离导致的重复类。

三、现场信息抓取(YARN 上先拿到"容器里到底有什么")

3.1 找到失败的容器日志

yarn logs -applicationId <appId> > app.log

或只抓某个 container:

yarn logs -applicationId <appId> -containerId <containerId>

重点看:

TaskManager 的 stderr/stdout(多数类问题发生在 TM)报错栈第一处 Caused by(最关键)

3.2 确认容器里加载了哪些 jar

在日志中搜索:

Classpath: / class path(不同版本可能打印位置不同)

Found jar / Adding jar / Using configuration 等关键词

如果日志不够,进入"增强日志"步骤(见第 五 节)。

四、快速自检清单(90% 的问题在这里)

4.1 你的依赖到底在哪里?
  • 作业 jar 是否是 fat jar?检查 jar 里是否有 BOOT-INF/lib(Spring Boot)或大量第三方包(jar tf xxx.jar | head)。
  • yarn.provided.lib.dirs 是否真正生效?配置是否在提交使用的 flink-conf.yaml 生效?是否指向 HDFS/本地且可被 YARN 访问?目录是否有读权限?
  • plugins/ 是否放了 connector(kafka、hudi、iceberg)?是否与作业 jar 重复?
4.2 依赖是否"成组"出现(常见坑)

很多库不是单 jar 就能跑,典型例子:

OkHttp 4.x:需要 Okio、Kotlin stdlib(且可能需要 jdk7/jdk8 扩展包,视版本而定)

Jackson:core / databind / annotations 版本需对齐

Netty:一套版本需对齐(Flink 自带,用户不要随便覆盖)

经验:如果用 provided 方式提供依赖,请按依赖树把传递依赖一起放齐,并保持版本一致。

五、开启类加载调试日志(定位"到底从哪个 jar 加载的")

在提交或配置中增加(不同日志框架版本可能是 log4j/log4j2,原则相同):

-Dlog4j.logger.org.apache.flink.runtime.classloading=DEBUG

你会在日志里看到类似:

  • 某某 ClassLoader 加载了 com.xxx.Foo,来源 jar 路径
  • 哪些 jar 被加入用户类路径

这一步可以直接回答:

  • 类究竟来自"作业 jar"还是 "provided/lib"
  • 是否出现一份类被加载了两次(两个不同 ClassLoader)

六、用"依赖树"确认应该有什么(本地就能做)

Maven

mvn -q dependency:tree > dep.tree.txt

针对某个库过滤:

mvn -q dependency:tree | grep -E "okhttp|okio|kotlin|jackson|netty"

Gradle

./gradlew dependencies --configuration runtimeClasspath > deps.txt

你要确认两件事:

  • 运行时依赖是否齐全(ClassNotFoundException常用,简称 CNF)
  • 同一 group/artifact 是否出现多个版本(冲突常用)

七、处理策略:缺依赖 vs 冲突(给出可选解)

7.1 缺依赖(CNF / NoClassDefFoundError)

优先推荐顺序:

  • 直接打 fat jar(最稳),用 shade/assembly 把运行时依赖都打进作业 jar(排除 Flink 自带依赖,如 flink-*, scala-*, slf4j/log4j 等)。
  • 或补齐 provided 目录依赖(成组补齐),不要只放一个 okhttp.jar,要把其传递依赖也放进去。
  • 校验 yarn.provided.lib.dirs 配置是否对当前 job 生效(常见是改了配置但提交用的是另一个 conf)。
7.2 版本冲突(NoSuchMethodError / IncompatibleClassChangeError)

可选处理:

  • 消除重复来源:不要同时在作业 jar 和 provided/flink lib 放同一依赖
  • 锁版本:用 dependencyManagement / Gradle constraints 固定版本
  • 必要时用 shading relocation:把你自己的三方库 relocate 到私有命名空间(例如 com.myjob.shaded.okhttp3...),避免和集群侧冲突
  • 很少数场景:用 classloader.parent-first-patterns.additional 强制 parent-first(谨慎使用,容易引入另一类冲突)
7.3 类加载器重复导致 ClassCastException

典型解决:

  • 确保同一个库只在一个"层级"出现:要么只在作业 jar,要么只在 provided/plugin
  • connector/plugin 与作业 jar 里不要重复放同款依赖
  • 必要时对重复库做 shading relocation
  • 只在本地 IDE 跑通,YARN 上 CNF。原因:本地 classpath 完整,YARN 容器缺 jar;或 provided 目录没生效/路径权限问题。
  • JM 正常,TM 报缺类。原因:用户 jar 分发到 TM 失败、或 TM 侧 classpath 不同、或动态加载的算子在 TM 初始化时报错。
  • 升级作业依赖后出现 NoSuchMethodError。原因:集群 lib/provided 中仍然是旧版本,child/parent 加载顺序导致混用。
  • OkHttp/Kotlin 相关。OkHttp 4.x 引入 Kotlin 生态依赖,provided 只放 okhttp 经常缺类;需要按依赖树补齐(kotlin-stdlib、okio、annotations 等),并注意版本对齐。

九、建议的落地规范(减少以后踩坑)

  • 明确约定:集群 lib/provided 放什么、作业 jar 放什么(二选一,尽量别混)
  • 作业构建时输出:
  1. dependency:tree(留档)
  2. 最终 jar 清单(jar tf 或构建报告)
  • 对"高频冲突库"建立黑名单/白名单:netty、jackson、kafka client、guava、protobuf、kotlin 等
  • 集群升级/依赖变更时同步更新 provided,并做回归测试
相关推荐
俊哥大数据11 小时前
【项目10】基于Flink房地产领域大数据实时分析系统
大数据·flink
Hello.Reader12 小时前
Flink CEP Pattern API、连续性、跳过策略、超时与迟到数据一篇讲透
大数据·flink
俊哥大数据18 小时前
【项目7】 基于Flink新闻资讯大数据推荐系统
大数据·flink
Hello.Reader19 小时前
Flink State Processor API 读写/修复 Savepoint,把“状态”当成可查询的数据
大数据·flink
lpfasd1231 天前
鲲鹏生态下的实时计算新选择:OmniStream深度解析与竞品横向对比
大数据·flink
Jackyzhe1 天前
Flink源码阅读:JobManager的HA机制
大数据·flink
Jackeyzhe2 天前
Flink源码阅读:Netty通信
flink
面向Google编程2 天前
Flink源码阅读:JobManager的HA机制
大数据·flink
用户7227868123442 天前
Flink源码阅读:Task数据交互
flink