Apache Spark - 使用spark-submit抛出NoSuchMethodError

时间:2017-02-22 17:45:50

标签: java guava nosuchmethoderror maven-shade-plugin spark-submit

要将Spark应用程序提交到群集,他们的文档说明:

  

要执行此操作,请创建包含代码及其依赖项的程序集jar(或“uber”jar)。 sbt和Maven都有汇编插件。在创建程序集jar时,将Spark和Hadoop列为提供的依赖项;这些不需要捆绑,因为它们是由集群管理器在运行时提供的。 - http://spark.apache.org/docs/latest/submitting-applications.html

所以,我在我的pom.xml文件中添加了Apache Maven Shade插件。 (3.0.0版)
我将Spark依赖的范围转换为provided。 (版本2.1.0)

(我还添加了Apache Maven Assembly Plugin,以确保在运行mvn clean package时我将所有依赖项包装在jar中。我不确定它是否真的有必要。)

spark-submit失败的方式就是这样。它为我所拥有的依赖项抛出NoSuchMethodError(请注意,代码在IntelliJ内部编译时从本地实例开始工作,假设已删除provided)。

Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.base.Stopwatch.createStarted()Lcom/google/common/base/Stopwatch;

抛出错误的代码行无关紧要 - 它只是我的main方法中的第一行创建了Stopwatch部分Google Guava实用程序。 (第21.0版)

其他在线解决方案表明它与Guava的版本冲突有关,但我对这些建议还没有任何运气。任何帮助将不胜感激,谢谢。

3 个答案:

答案 0 :(得分:5)

如果您查看Spark 2.1.0安装的/jars子目录,您可能会看到guava-14.0.1.jar。根据您使用的API for the Guava Stopwatch#createStarted method,在{2006}之前,createStarted不存在。最有可能发生的事情是Spark进程Classloader在找到你的uberjar中打包的Guava 21.0库之前找到Spark提供的Guava 14.0.1库。

一种可能的解决方案是使用class-relocation feature provided by the Maven Shade plugin(你已经用它来构建你的uberjar)。通过“类重定位”,Maven-Shade在uberjar的包装过程中移动Guava 21.0类(代码需要)从pattern位置反映其现有包名(例如com.google.common.base)到任意您在Shade配置中指定的shadedPattern位置(例如myguava123.com.google.common.base)。

结果是较旧和较新的Guava库不再共享包名,从而避免了运行时冲突。

答案 1 :(得分:2)

很可能你有依赖冲突,是的。

首先,您可以查看构建jar时是否存在依赖冲突。一个快速的方法是直接查看你的jar,看看是否有Stopwatch.class文件,如果通过查看字节码,似乎方法createStarted就在那里。 否则,您还可以列出依赖关系树并从那里开始工作:https://maven.apache.org/plugins/maven-dependency-plugin/examples/resolving-conflicts-using-the-dependency-tree.html

如果您的jar不是问题,那么由于您的spark安装和jar之间存在冲突,您可能会遇到依赖性问题。 查看spark安装的lib和jars文件夹。在那里你可以看到你是否有包含备用版番石榴的罐子,它不支持秒表中的方法createStarted()

答案 2 :(得分:0)

通过以下配置应用以上答案来解决问题:

  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
      <execution>
        <phase>package</phase>
        <goals>
          <goal>shade</goal>
        </goals>
        <configuration>
            <relocations>
                <relocation>
                    <pattern>com.google.common</pattern>
                    <shadedPattern>shade.com.google.common</shadedPattern>
                </relocation>
                <relocation>
                    <pattern>com.google.thirdparty.publicsuffix</pattern>
                    <shadedPattern>shade.com.google.thirdparty.publicsuffix</shadedPattern>
                </relocation>
          </relocations>
        </configuration>
      </execution>
    </executions>
  </plugin>