Spark连接BigQuery触发java.lang.NoSuchMethodError异常求助
这个NoSuchMethodError确实是Guava版本不兼容导致的——com.google.common.base.Optional.toJavaUtil()方法是在Guava 21.0版本才新增的,而你本地Spark 2.3.x默认自带的Guava版本(一般是14.0.1)根本没有这个方法。
虽然你已经在sbt里排除了依赖并指定了新版本Guava,但问题出在Spark启动时的类加载顺序:本地Spark的classpath里自带的旧Guava会优先于你的fat jar里的新版本被加载,导致运行时调用了旧类的方法。
下面是几个可行的解决方法:
1. 强制Spark优先加载你的fat jar中的类
修改spark-submit命令,通过--driver-class-path参数把你的jar放在类路径最前面,确保驱动端优先加载你打包的新版本Guava:
spark-submit --deploy-mode client --driver-class-path BigQueryConnector.jar --class com.bigquery.OwnDataSetReader BigQueryConnector.jar
这个方法不需要修改Spark的安装文件,对其他任务无影响,是最推荐的临时解决方案。
2. 替换Spark自带的Guava jar
如果你希望所有本地Spark任务都使用新版本Guava,可以直接替换Spark安装目录下的旧Guava包:
- 找到Spark安装路径下的
jars文件夹,删除其中的guava-*.jar(比如guava-14.0.1.jar) - 把你使用的
guava-30.0-jre.jar复制到这个jars文件夹中
注意:这个操作会影响所有本地Spark任务,如果其他任务依赖旧版本Guava的特性,可能会引发新的问题,需要谨慎测试。
3. 强化sbt依赖管理,确保编译期完全统一Guava版本
在你的build.sbt中添加dependencyOverrides,强制所有依赖库都使用指定的Guava版本,避免编译期出现潜在的版本不一致:
name := "bigquer-connector" version := "0.1" scalaVersion := "2.11.8" test in assembly := {} assemblyJarName in assembly := "BigQueryConnector.jar" assemblyMergeStrategy in assembly := { case x if x.startsWith("META-INF") => MergeStrategy.discard case x => val oldStrategy = (assemblyMergeStrategy in assembly).value oldStrategy(x) } // 强制所有依赖使用统一的Guava版本 dependencyOverrides += "com.google.guava" % "guava" % "30.0-jre" libraryDependencies += ("com.google.cloud.spark" %% "spark-bigquery" % "0.18.0") .exclude("com.google.guava", "guava") .exclude("org.glassfish.jersey.bundles.repackaged", "jersey-guava") libraryDependencies += "com.google.guava" % "guava" % "30.0-jre" libraryDependencies += ("org.apache.spark" % "spark-core_2.11" % "2.3.1") .exclude("com.google.guava", "guava") .exclude("org.glassfish.jersey.bundles.repackaged", "jersey-guava") libraryDependencies += ("org.apache.spark" % "spark-sql_2.11" % "2.3.1") .exclude("com.google.guava", "guava") .exclude("org.glassfish.jersey.bundles.repackaged", "jersey-guava")
调试验证
可以在你的主类中添加一行代码,打印运行时实际加载的Guava版本,确认问题是否解决:
object OwnDataSetReader extends App { // 打印Guava版本,验证是否加载了正确的版本 println("Loaded Guava version: " + com.google.common.base.Optional.class.getPackage().getImplementationVersion()) val session = SparkSession.builder() .appName("big-query-connector") .config(getConf) .getOrCreate() // ... 剩余代码不变 }
如果输出是30.0-jre,说明版本加载正确,问题应该就能解决了。
内容的提问来源于stack exchange,提问作者Anoop Deshpande

