Spark连接BigQuery触发java.lang.NoSuchMethodError异常求助
我之前在Spark 2.3.x环境下用BigQuery Connector时也踩过这个坑!这个NoSuchMethodError的本质不是你打包的fat jar里有Guava版本冲突,而是Spark 2.3.x默认自带的Guava版本太低(14.0.1左右),而你用的spark-bigquery 0.18.0依赖的Guava 30.0-jre才有com.google.common.base.Optional.toJavaUtil()这个方法——关键是Spark运行时会优先加载自身classpath里的旧Guava,哪怕你的jar里已经打包了高版本也没用。
下面给你两个可行的解决方案,覆盖快速测试到生产环境场景:
方案1:本地模式快速验证——强制优先加载你的jar类
如果只是本地测试,可以通过spark-submit的参数让driver优先使用你fat jar里的类,跳过Spark自带的旧Guava:
spark-submit \ --deploy-mode client \ --conf spark.driver.userClassPathFirst=true \ --class com.bigquery.OwnDataSetReader \ BigQueryConnector.jar
或者直接指定driver的classpath,把你的jar放在最前面:
spark-submit \ --deploy-mode client \ --driver-class-path BigQueryConnector.jar \ --class com.bigquery.OwnDataSetReader \ BigQueryConnector.jar
⚠️ 注意:spark.driver.userClassPathFirst=true可能会引发其他依赖冲突(比如Spark自带的其他库和你jar里的版本不兼容),所以这个方法只适合本地快速验证,生产环境不推荐。
方案2:生产环境推荐——用SBT Shade重命名Guava包
最稳妥的方法是在打包时把你依赖的Guava包重命名,让它和Spark自带的Guava完全隔离。修改你的build.sbt,添加Shade规则:
name := "bigquer-connector" version := "0.1" scalaVersion := "2.11.8" test in assembly := {} assemblyJarName in assembly := "BigQueryConnector.jar" // 新增Shade规则,将Guava包重命名为shaded.com.google.common assemblyShadeRules in assembly := Seq( ShadeRule.rename("com.google.common.**" -> "shaded.com.google.common.@1") .inLibrary("com.google.guava" % "guava" % "30.0-jre") .inLibrary("com.google.cloud.spark" %% "spark-bigquery" % "0.18.0") .inProject ) assemblyMergeStrategy in assembly := { case x if x.startsWith("META-INF") => MergeStrategy.discard case x => val oldStrategy = (assemblyMergeStrategy in assembly).value oldStrategy(x) } 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")
修改后重新执行sbt assembly打包,这样你的fat jar里的Guava类都会被重命名为shaded.com.google.common开头,和Spark自带的com.google.common完全隔离开,从根源上解决版本冲突问题。
验证打包结果
打包完成后,你可以用下面的命令确认Guava包已经被成功重命名:
jar tf BigQueryConnector.jar | grep shaded.com.google.common
如果输出一堆以shaded.com.google.common开头的类,就说明打包成功了。
内容的提问来源于stack exchange,提问作者Anoop Deshpande

