Spark 在使用 SBT 程序集 JAR 提交的 spark 中找不到应用程序 class 本身 (ClassNotFoundException)
Spark can't find the application class itself (ClassNotFoundException) in spark-submit with SBT assembly JAR
背景
我正在尝试使用 Scala 开始使用 Spark。
最初,我试图编写一个流式 Kinesis 消费者,然后 this official example。
虽然,在这一点上我已经减少了我的错误案例以删除任何与 Kinesis 相关的东西,除了包依赖性,并且错误保持不变。
我使用 SBT 生成了我项目的程序集 JAR。然后,我尝试使用 spark-submit
在本地 运行 它。 (下面有详细步骤。)
这一直失败并显示 ClassNotFoundException
,声称它找不到我的应用程序的主要 class。 (下面有详细的输出。)
我要强调一下:
据我了解,我不相信这和其他发帖人看到的ClassNotFoundException
是一样的,我也不相信这个问题是这些问题的重复。
特别是,据我所知:
- 有问题的 class 是我的主要应用程序 class 本身。
- 我引用的是正确的完全限定 class 名称。
- 我已通过 SBT 生成程序集 JAR 将依赖项包含在输出 JAR 中。
重现步骤
- 初始化空的 Scala SBT 项目。 (我在 IntelliJ 中使用了默认模板,但我认为这并不重要。)
- 添加项目代码。 (包括在下面。)
sbt assembly
./bin/spark-submit --class "sparkpoc.KinesisExample" --master local[4] ~/git/ming-spark-poc/target/scala-2.11/ming-spark-poc-assembly-0.1.jar
(适当替换。)
错误信息
$ ./bin/spark-submit --class "sparkpoc.KinesisExample" --master local[4] ~/git/ming-spark-poc/target/scala-2.11/ming-spark-poc-assembly-0.1.jar
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (file:/Users/ming/misc/spark-2.3.0-bin-hadoop2.7/jars/hadoop-auth-2.7.3.jar) to method sun.security.krb5.Config.getInstance()
WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2018-05-09 15:39:01 WARN NativeCodeLoader:62 - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
java.lang.ClassNotFoundException: sparkpoc.KinesisExample
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:466)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:566)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:374)
at org.apache.spark.util.Utils$.classForName(Utils.scala:235)
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:836)
at org.apache.spark.deploy.SparkSubmit$.doRunMain(SparkSubmit.scala:197)
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:227)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:136)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
2018-05-09 15:39:01 INFO ShutdownHookManager:54 - Shutdown hook called
2018-05-09 15:39:01 INFO ShutdownHookManager:54 - Deleting directory /private/var/folders/py/jrf50pwj1xdd4grjvlg07g580000gp/T/spark-c5f3bade-fbfe-4516-900e-99fee1b47366
我的代码
build.sbt
name := "ming-spark-poc"
version := "0.1"
scalaVersion := "2.11.8"
val sparkVersion = "2.3.0"
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-sql" % sparkVersion % "provided",
"org.apache.spark" %% "spark-streaming" % sparkVersion % "provided",
"org.apache.spark" %% "spark-streaming-kinesis-asl" % sparkVersion
)
assemblyOption in assembly := (assemblyOption in assembly).value
.copy(includeScala = false)
assemblyMergeStrategy in assembly := {
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
case _ => MergeStrategy.first
}
我知道在生产代码中设置 assemblyMergeStrategy
没有默认大小写回退是不好的做法。这只是构建项目的快速破解,据我所知,它与我当前的错误无关。
assembly.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
KinesisExample.scala
最初,这是一个 Kinesis 消费者。
它已被简化为一个什么都不做的占位符应用程序。
错误没有改变。
package sparkpoc
import scala.collection.mutable
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
object KinesisExample {
def main(args: Array[String]): Unit = {
val batchInterval = Seconds(5)
val sparkConf = new SparkConf().setAppName("SparcPocKinesisExample")
val streamingContext = new StreamingContext(sparkConf, batchInterval)
streamingContext.start()
streamingContext.awaitTermination()
}
}
到目前为止我尝试了什么
我可以运行来自预先打包的 JAR 的官方示例,看起来没有问题。
$ ./bin/run-example SparkPi 10
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (file:/Users/ming/misc/spark-2.3.0-bin-hadoop2.7/jars/hadoop-auth-2.7.3.jar) to method sun.security.krb5.Config.getInstance()
WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2018-05-09 16:14:07 WARN NativeCodeLoader:62 - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
2018-05-09 16:14:08 INFO SparkContext:54 - Running Spark version 2.3.0
2018-05-09 16:14:08 INFO SparkContext:54 - Submitted application: Spark Pi
<SNIPPED>
据我所知,生成的 JAR 文件包含预期的 class 文件。我以两种不同的方式独立验证了这一点。
我用 jar -tf
检查了 JAR 的内容。据我所知,它在预期位置包含预期的 class 文件。
$ jar -tf ./target/scala-2.11/ming-spark-poc-assembly-0.1.jar | grep KinesisExample
org/apache/spark/examples/streaming/KinesisExampleUtils$$anonfun$getRegionNameByEndpoint.class
org/apache/spark/examples/streaming/KinesisExampleUtils$$anonfun$getRegionNameByEndpoint.class
org/apache/spark/examples/streaming/KinesisExampleUtils$$anonfun$getRegionNameByEndpoint.class
org/apache/spark/examples/streaming/KinesisExampleUtils$.class
org/apache/spark/examples/streaming/KinesisExampleUtils.class
sparkpoc/KinesisExample$.class
sparkpoc/KinesisExample.class
我用 unzip
提取了 JAR 的内容并手动检查了它们。据我所知,它在预期位置包含预期的 class 文件。
虽然我不希望此项目中的任何内容依赖于当前工作目录,但我使用 Spark 安装根目录作为当前工作目录重复了相同的步骤,结果没有任何变化。
我尝试 运行直接生成 JAR。虽然我不希望这对实际 运行 Spark 应用程序正常工作,但我认为它可以深入了解 class 分辨率的情况。失败如下。
$ java -jar ./target/scala-2.11/ming-spark-poc-assembly-0.1.jar "sparkpoc.KinesisExample"
Error: Could not find or load main class sparkpoc.KinesisExample
Caused by: java.lang.ClassNotFoundException: sparkpoc.KinesisExample
我尝试重命名 class 所在的包,包括一度将其放在顶层包中(没有 package
声明)。每次使用适当的完全限定 class 名称调用 spark-submit
,我仍然遇到相同的错误。
万一 assemblyMergeStrategy
黑客间接破坏了某些东西,我尝试用如下显式列表替换它。
assemblyMergeStrategy in assembly := {
case PathList("javax", "inject", _*) => MergeStrategy.last
case PathList("org", "apache", _*) => MergeStrategy.last
case PathList("org", "aopalliance", _*) => MergeStrategy.last
case PathList("mime.types") => MergeStrategy.last
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
我仍然遇到同样的错误。 编辑:这实际上按预期工作。我有一个过时的构建工件的单独问题。见下文。
问题
- 我当前的 SBT 设置是否有任何不正确或不合适的地方? (除了
assemblyMergeStrategy
中的黑客攻击。)
- 我还能如何验证生成的 JAR 是否正确?
- 假设生成的 JAR 是正确的,我还缺少什么可能导致这样的问题?
提前感谢您的任何见解或建议。
编辑:分辨率
下面 Ahmad Ragab 的回答是正确的。
我的 assemblyMergeStrategy
确实有问题,导致输出 JAR 格式错误。就我的理解而言,我相信我在 assemblyMergeStrategy
中用过于激进的通配符破坏了一些重要的元数据。有效的版本如下。
assemblyMergeStrategy in assembly := {
case PathList("javax", "inject", _*) => MergeStrategy.last
case PathList("org", "apache", _*) => MergeStrategy.last
case PathList("org", "aopalliance", _*) => MergeStrategy.last
case PathList("mime.types") => MergeStrategy.last
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
值得注意的是,我以前尝试过一次,但不知何故不起作用。我的怀疑是,虽然我无法追溯地证明这一点,但我不小心用陈旧的构建工件测试了那个变化,所以我不小心 运行 使用了没有这些变化的旧版本。在清理所有内容并使用这些新更改进行重建后,它按预期工作。
(请注意,由于没有定义的输出,应用程序仍然会在启动时崩溃,但当然,这种失败是意料之中的,我们已经删除了意外的失败。)
这看起来有些奇怪 assemblyMergeStrategy
尝试:
assemblyMergeStrategy in assembly := {
case PathList("META-INF", _@_*) => MergeStrategy.discard
case _ => MergeStrategy.first
}
其次,您可能需要在程序集选项中显式设置您的主要 class 以便创建正确的清单,但公平地说,我无法证明这一点。过去我做了以下一些成功的事情
mainClass in assembly := Some("sparkpoc.KinesisExample")
最后,您可以确认您的 jar 是否已正确创建的一种方法是执行此操作:
java -classpath ming-spark-poc-assembly-0.1.jar "sparkpoc.KinesisExample"
希望其中一些能引导您朝着正确的方向前进。
背景
我正在尝试使用 Scala 开始使用 Spark。
最初,我试图编写一个流式 Kinesis 消费者,然后 this official example。 虽然,在这一点上我已经减少了我的错误案例以删除任何与 Kinesis 相关的东西,除了包依赖性,并且错误保持不变。
我使用 SBT 生成了我项目的程序集 JAR。然后,我尝试使用 spark-submit
在本地 运行 它。 (下面有详细步骤。)
这一直失败并显示 ClassNotFoundException
,声称它找不到我的应用程序的主要 class。 (下面有详细的输出。)
我要强调一下:
据我了解,我不相信这和其他发帖人看到的ClassNotFoundException
是一样的,我也不相信这个问题是这些问题的重复。
特别是,据我所知:
- 有问题的 class 是我的主要应用程序 class 本身。
- 我引用的是正确的完全限定 class 名称。
- 我已通过 SBT 生成程序集 JAR 将依赖项包含在输出 JAR 中。
重现步骤
- 初始化空的 Scala SBT 项目。 (我在 IntelliJ 中使用了默认模板,但我认为这并不重要。)
- 添加项目代码。 (包括在下面。)
sbt assembly
./bin/spark-submit --class "sparkpoc.KinesisExample" --master local[4] ~/git/ming-spark-poc/target/scala-2.11/ming-spark-poc-assembly-0.1.jar
(适当替换。)
错误信息
$ ./bin/spark-submit --class "sparkpoc.KinesisExample" --master local[4] ~/git/ming-spark-poc/target/scala-2.11/ming-spark-poc-assembly-0.1.jar
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (file:/Users/ming/misc/spark-2.3.0-bin-hadoop2.7/jars/hadoop-auth-2.7.3.jar) to method sun.security.krb5.Config.getInstance()
WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2018-05-09 15:39:01 WARN NativeCodeLoader:62 - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
java.lang.ClassNotFoundException: sparkpoc.KinesisExample
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:466)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:566)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:374)
at org.apache.spark.util.Utils$.classForName(Utils.scala:235)
at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:836)
at org.apache.spark.deploy.SparkSubmit$.doRunMain(SparkSubmit.scala:197)
at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:227)
at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:136)
at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
2018-05-09 15:39:01 INFO ShutdownHookManager:54 - Shutdown hook called
2018-05-09 15:39:01 INFO ShutdownHookManager:54 - Deleting directory /private/var/folders/py/jrf50pwj1xdd4grjvlg07g580000gp/T/spark-c5f3bade-fbfe-4516-900e-99fee1b47366
我的代码
build.sbt
name := "ming-spark-poc"
version := "0.1"
scalaVersion := "2.11.8"
val sparkVersion = "2.3.0"
libraryDependencies ++= Seq(
"org.apache.spark" %% "spark-sql" % sparkVersion % "provided",
"org.apache.spark" %% "spark-streaming" % sparkVersion % "provided",
"org.apache.spark" %% "spark-streaming-kinesis-asl" % sparkVersion
)
assemblyOption in assembly := (assemblyOption in assembly).value
.copy(includeScala = false)
assemblyMergeStrategy in assembly := {
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
case _ => MergeStrategy.first
}
我知道在生产代码中设置 assemblyMergeStrategy
没有默认大小写回退是不好的做法。这只是构建项目的快速破解,据我所知,它与我当前的错误无关。
assembly.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
KinesisExample.scala
最初,这是一个 Kinesis 消费者。 它已被简化为一个什么都不做的占位符应用程序。 错误没有改变。
package sparkpoc
import scala.collection.mutable
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
object KinesisExample {
def main(args: Array[String]): Unit = {
val batchInterval = Seconds(5)
val sparkConf = new SparkConf().setAppName("SparcPocKinesisExample")
val streamingContext = new StreamingContext(sparkConf, batchInterval)
streamingContext.start()
streamingContext.awaitTermination()
}
}
到目前为止我尝试了什么
我可以运行来自预先打包的 JAR 的官方示例,看起来没有问题。
$ ./bin/run-example SparkPi 10
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.apache.hadoop.security.authentication.util.KerberosUtil (file:/Users/ming/misc/spark-2.3.0-bin-hadoop2.7/jars/hadoop-auth-2.7.3.jar) to method sun.security.krb5.Config.getInstance()
WARNING: Please consider reporting this to the maintainers of org.apache.hadoop.security.authentication.util.KerberosUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2018-05-09 16:14:07 WARN NativeCodeLoader:62 - Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
2018-05-09 16:14:08 INFO SparkContext:54 - Running Spark version 2.3.0
2018-05-09 16:14:08 INFO SparkContext:54 - Submitted application: Spark Pi
<SNIPPED>
据我所知,生成的 JAR 文件包含预期的 class 文件。我以两种不同的方式独立验证了这一点。
我用 jar -tf
检查了 JAR 的内容。据我所知,它在预期位置包含预期的 class 文件。
$ jar -tf ./target/scala-2.11/ming-spark-poc-assembly-0.1.jar | grep KinesisExample
org/apache/spark/examples/streaming/KinesisExampleUtils$$anonfun$getRegionNameByEndpoint.class
org/apache/spark/examples/streaming/KinesisExampleUtils$$anonfun$getRegionNameByEndpoint.class
org/apache/spark/examples/streaming/KinesisExampleUtils$$anonfun$getRegionNameByEndpoint.class
org/apache/spark/examples/streaming/KinesisExampleUtils$.class
org/apache/spark/examples/streaming/KinesisExampleUtils.class
sparkpoc/KinesisExample$.class
sparkpoc/KinesisExample.class
我用 unzip
提取了 JAR 的内容并手动检查了它们。据我所知,它在预期位置包含预期的 class 文件。
虽然我不希望此项目中的任何内容依赖于当前工作目录,但我使用 Spark 安装根目录作为当前工作目录重复了相同的步骤,结果没有任何变化。
我尝试 运行直接生成 JAR。虽然我不希望这对实际 运行 Spark 应用程序正常工作,但我认为它可以深入了解 class 分辨率的情况。失败如下。
$ java -jar ./target/scala-2.11/ming-spark-poc-assembly-0.1.jar "sparkpoc.KinesisExample"
Error: Could not find or load main class sparkpoc.KinesisExample
Caused by: java.lang.ClassNotFoundException: sparkpoc.KinesisExample
我尝试重命名 class 所在的包,包括一度将其放在顶层包中(没有 package
声明)。每次使用适当的完全限定 class 名称调用 spark-submit
,我仍然遇到相同的错误。
万一 assemblyMergeStrategy
黑客间接破坏了某些东西,我尝试用如下显式列表替换它。
assemblyMergeStrategy in assembly := {
case PathList("javax", "inject", _*) => MergeStrategy.last
case PathList("org", "apache", _*) => MergeStrategy.last
case PathList("org", "aopalliance", _*) => MergeStrategy.last
case PathList("mime.types") => MergeStrategy.last
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
我仍然遇到同样的错误。 编辑:这实际上按预期工作。我有一个过时的构建工件的单独问题。见下文。
问题
- 我当前的 SBT 设置是否有任何不正确或不合适的地方? (除了
assemblyMergeStrategy
中的黑客攻击。) - 我还能如何验证生成的 JAR 是否正确?
- 假设生成的 JAR 是正确的,我还缺少什么可能导致这样的问题?
提前感谢您的任何见解或建议。
编辑:分辨率
下面 Ahmad Ragab 的回答是正确的。
我的 assemblyMergeStrategy
确实有问题,导致输出 JAR 格式错误。就我的理解而言,我相信我在 assemblyMergeStrategy
中用过于激进的通配符破坏了一些重要的元数据。有效的版本如下。
assemblyMergeStrategy in assembly := {
case PathList("javax", "inject", _*) => MergeStrategy.last
case PathList("org", "apache", _*) => MergeStrategy.last
case PathList("org", "aopalliance", _*) => MergeStrategy.last
case PathList("mime.types") => MergeStrategy.last
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
值得注意的是,我以前尝试过一次,但不知何故不起作用。我的怀疑是,虽然我无法追溯地证明这一点,但我不小心用陈旧的构建工件测试了那个变化,所以我不小心 运行 使用了没有这些变化的旧版本。在清理所有内容并使用这些新更改进行重建后,它按预期工作。
(请注意,由于没有定义的输出,应用程序仍然会在启动时崩溃,但当然,这种失败是意料之中的,我们已经删除了意外的失败。)
这看起来有些奇怪 assemblyMergeStrategy
尝试:
assemblyMergeStrategy in assembly := {
case PathList("META-INF", _@_*) => MergeStrategy.discard
case _ => MergeStrategy.first
}
其次,您可能需要在程序集选项中显式设置您的主要 class 以便创建正确的清单,但公平地说,我无法证明这一点。过去我做了以下一些成功的事情
mainClass in assembly := Some("sparkpoc.KinesisExample")
最后,您可以确认您的 jar 是否已正确创建的一种方法是执行此操作:
java -classpath ming-spark-poc-assembly-0.1.jar "sparkpoc.KinesisExample"
希望其中一些能引导您朝着正确的方向前进。