Dataproc Spark集群运行Spark作业报NoSuchMethodError本地运行正常
故障原因
java.lang.NoSuchMethodError为典型的运行时依赖版本不匹配报错,本地运行与Dataproc集群运行结果不一致的核心原因是两种场景的类加载优先级逻辑不同:
- 本地执行
java -jar JARFILENAME.jar时,JVM默认优先加载JAR包内打包的所有依赖类,本地编译时绑定的Spark、Deequ版本完全匹配,因此运行正常 - 提交到Dataproc Spark集群运行时,Spark默认采用集群优先的类加载策略,会优先加载Dataproc镜像预装在集群系统路径下的Spark生态组件JAR包,不会优先读取你提交的JAR内打包的Spark相关类。从报错栈可以看到,作业使用的Amazon Deequ组件在调用Spark SQL Catalyst模块的
ApproximatePercentile$PercentileDigest.getPercentiles方法时,集群实际加载的Spark版本中该方法的签名或存在性与Deequ编译时绑定的版本不一致,因此抛出方法不存在的错误。
注:Amazon Deequ的每个发布版本都严格绑定对应Spark大版本,跨Spark版本使用时大概率触发此类方法签名不兼容问题。
排查步骤
- 查看本地Maven项目
pom.xml中声明的Spark系列依赖(spark-core、spark-sql、spark-catalyst)版本、Deequ依赖版本 - 登录Dataproc集群主节点,执行
spark-submit --version查询集群预装的Spark准确版本,也可以直接对照Dataproc镜像版本发行说明确认内置组件版本 - 核对版本匹配关系:确认你引入的Deequ版本是否为适配集群当前Spark版本的发行版,绝大多数此类故障都是本地编译依赖的Spark版本与集群内置Spark版本不一致导致。
解决方案
- 优先选择版本对齐方案:将本地Maven项目中所有Spark相关依赖的
scope设置为provided(打包时不将Spark依赖打入JAR,运行时直接使用集群内置版本),将Spark依赖版本调整为与集群内置Spark版本完全一致,同时更换为对应该Spark版本的Deequ发行版,重新打包后提交作业即可,该方案无兼容性副作用。 - 临时快速修复方案:提交Spark作业时追加如下配置,强制Spark优先加载用户提交JAR内的类,跳过集群内置的对应依赖:
--conf spark.driver.userClassPathFirst=true --conf spark.executor.userClassPathFirst=true
该方案可能触发其他依赖冲突问题,仅建议测试环境使用。
- 特殊场景适配方案:如果必须使用指定版本的Deequ、无法对齐集群Spark版本,可以通过maven-shade-plugin在打包时对Spark Catalyst相关冲突类做路径重定位,将依赖的类重写到自定义包路径下,彻底避免与集群内置类的路径冲突,该方案配置成本较高,仅建议以上方案都不生效时使用。
注意:本地直接执行JAR的运行环境与Spark集群分布式运行环境的类加载逻辑存在本质差异,本地运行通过不代表集群环境依赖兼容,Spark作业打包时默认将Spark核心依赖设置为provided scope是标准实践。
内容的提问来源于stack exchange,提问作者Kasper
相关产品推荐
相关产品推荐

