为什么Spark应用程序失败,而#34;线程"主要" java.lang.NoClassDefFoundError:... StringDeserializer"?

时间:2017-06-25 17:41:40

标签: java maven apache-spark spark-streaming apache-kafka-streams

我正在开发一个使用Spark和Java监听Kafka流的Spark应用程序。

我使用kafka_2.10-0.10.2.1。

我为Kafka属性设置了各种参数:bootstrap.serverskey.deserializervalue.deserializer等。

我的应用程序编译得很好,但是当我提交它时,它失败并出现以下错误:

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/kafka/common/serialization/StringDeserializer

我对StringDeserializerkey.deserializer使用value.deserializer,所以它确实与我编写应用程序的方式有关。

pom.xml中使用的各种maven依赖项:

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
        <version>2.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-streaming_2.10</artifactId>
        <version>2.1.1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
        <version>2.1.1</version>
    </dependency>

我尝试更新spark streaming / kafka的版本。我找不到任何地方。

4 个答案:

答案 0 :(得分:1)

  

火花streaming_的 2.10

这取决于Scala 2.10

您的其他依赖项正在使用Scala 2.11

Upgrading the version是当前错误的正确解决方案。

并确保在streaming-kafka- 0-10 内,这与您正在运行的Kafka版本相匹配

  

应用程序编译正常但是当我尝试提交spark作业时,它显示错误:线程“main”中的异常java.lang.NoClassDefFoundError:

By default, maven does not include dependency jars when it builds a target

答案 1 :(得分:1)

正如您在the comment above中提到的那样:

  

原来问题是超级罐没有正确构建。

这正是问题所在。它确实与你如何组装你的Spark应用程序有关,我担心你可能选择了超级jar方式。在我看来,这是浪费你的集合时间和火花提交时间。

我个人更喜欢使用--packages命令行选项,该选项负责在需要时提取所有必需的依赖项。

$ ./bin/spark-submit --help
...
  --packages                  Comma-separated list of maven coordinates of jars to include
                              on the driver and executor classpaths. Will search the local
                              maven repo, then maven central and any additional remote
                              repositories given by --repositories. The format for the
                              coordinates should be groupId:artifactId:version.
...

这使您作为Spark开发人员的生活更轻松,您不再需要等到maven / sbt下载依赖项并将它们组合在一起。这是在spark-submit时间完成的(也许这也是别人的工作!))

您应该spark-submit如下:

spark-submit --packages org.apache.spark:spark-streaming-kafka-0-10_2.11:2.1.1 ...

这个额外要求的原因是在Spark的CLASSPATH中默认不包含spark-streaming-kafka-0-10模块(因为大多数情况下它被认为是不必要的)。通过执行上述--packages命令行,您可以触发加载模块(具有传递依赖性)。

您不应该将模块捆绑在Spark应用程序的超级jar中。

答案 2 :(得分:1)

原来的问题是uber jar无法正确构建。 如果您想组装该应用程序并打包一个超级罐子。

src/assembly/assembly.xml中创建一个程序集文件

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
    <id>bin</id>
    <formats>
        <format>jar</format>
    </formats>
    <includeBaseDirectory>false</includeBaseDirectory>
    <dependencySets>
        <dependencySet>
            <unpack>true</unpack>
            <scope>provided</scope>
        </dependencySet>
    </dependencySets>
</assembly>

然后将maven-assembly-plugin添加到pom.xml


        <plugins>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.3.0</version>
                <configuration>
                    <descriptors>
                        <descriptor>src/assembly/assembly.xml</descriptor>
                    </descriptors>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

如果要向uber jar添加依赖项,只需向其添加提供的作用域即可。
在您的情况下,将是这样的:


    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
        <version>2.1.1</version>
    </dependency>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-streaming_2.10</artifactId>
        <version>2.1.1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-streaming-kafka-0-10_2.11</artifactId>
        <version>2.1.1</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.apache.kafka</groupId>
        <artifactId>kafka-clients</artifactId>
        <version>0.10.2.1</version>
        <scope>provided</scope>
    </dependency>
$spark-submit --class Main application-bin.jar

答案 3 :(得分:0)

如果您更愿意使用阴影插件来生成胖罐子。 Jacek通过--packages方法提出的建议。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

您还可以使用maven-dependency插件获取一些依赖关系,并将其放在lib目录中的程序集中,然后提供给spark。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>3.1.2</version>
                <executions>
                    <execution>
                        <id>copy</id>
                        <phase>initialize</phase>
                        <goals>
                            <goal>copy</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <artifactItems>
                        <artifactItem>
                            <groupId>org.apache.logging.log4j</groupId>
                            <artifactId>log4j-core</artifactId>
                            <version>${log4j2.version}</version>
                            <type>jar</type>
                            <overWrite>true</overWrite>
                            <outputDirectory>${project.build.directory}/log4j-v2-jars</outputDirectory>
                            <destFileName>log4j-v2-core.jar</destFileName>
                        </artifactItem>
                        <artifactItem>
                            <groupId>org.apache.logging.log4j</groupId>
                            <artifactId>log4j-api</artifactId>
                            <version>${log4j2.version}</version>
                            <type>jar</type>
                            <overWrite>true</overWrite>
                            <outputDirectory>${project.build.directory}/log4j-v2-jars</outputDirectory>
                            <destFileName>log4j-v2-api.jar</destFileName>
                        </artifactItem>
                        <artifactItem>
                            <groupId>org.apache.logging.log4j</groupId>
                            <artifactId>log4j-1.2-api</artifactId>
                            <version>${log4j2.version}</version>
                            <type>jar</type>
                            <overWrite>true</overWrite>
                            <outputDirectory>${project.build.directory}/log4j-v2-jars</outputDirectory>
                            <destFileName>log4j-v2-1.2-api.jar</destFileName>
                        </artifactItem>
                        <artifactItem>
                            <groupId>org.apache.logging.log4j</groupId>
                            <artifactId>log4j-slf4j-impl</artifactId>
                            <version>${log4j2.version}</version>
                            <type>jar</type>
                            <overWrite>true</overWrite>
                            <outputDirectory>${project.build.directory}/log4j-v2-jars</outputDirectory>
                            <destFileName>log4j-v2-slf4j-impl.jar</destFileName>
                        </artifactItem>
                    </artifactItems>
                    <outputDirectory>${project.build.directory}/wars</outputDirectory>
                    <overWriteReleases>false</overWriteReleases>
                    <overWriteSnapshots>true</overWriteSnapshots>
                </configuration>
            </plugin>

之所以提出这一建议,是因为在您的情况下(就像我的工作一样),您的群集位于非常严格的防火墙之后,并且不允许在提交步骤与Spark联系以解决软件包。在这种情况下,您确实需要在进行人工制品准备时进行处理,而这两种方法中的任何一种都可能对您有所帮助。

在我的具有maven依赖性的示例中,我获取log4jv2并将其传递给spark 2.3,以获得log4j-v2日志输出(您可以放置​​依赖性)。