为什么Spark on AWS EMR不会从应用程序胖jar加载类?

时间:2017-05-12 10:15:02

标签: apache-spark classpath emr amazon-emr

我的Spark应用程序无法在AWS EMR集群上运行。我注意到这是因为某些类是从EMR设置的路径加载而不是从我的应用程序jar加载的。例如

java.lang.NoSuchMethodError: org.apache.avro.Schema$Field.<init>(Ljava/lang/String;Lorg/apache/avro/Schema;Ljava/lang/String;Ljava/lang/Object;)V
        at com.sksamuel.avro4s.SchemaFor$.fieldBuilder(SchemaFor.scala:424)
        at com.sksamuel.avro4s.SchemaFor$.fieldBuilder(SchemaFor.scala:406)

此处 org.apache.avro.Schema 是从 “jar:file:/ usr / lib / spark / jars / avro-加载的1.7.7.jar!/org/apache/avro/Schema.class“

com.sksamuel.avro4s取决于avro 1.8.1。我的应用程序是作为一个胖罐子和avro 1.8.1。为什么不加载?而不是从EMR设置类路径中选择1.7.7。

这只是一个例子。我在我的应用程序中包含的其他库中也看到了相同的内容。可能是Spark取决于1.7.7而且在包含其他依赖项时我必须加以遮挡。但为什么我的app jar中包含的类没有先加载?

2 个答案:

答案 0 :(得分:1)

经过一番阅读后,我意识到这就是Spark中类加载的工作方式。有一个钩子可以改变这种行为 spark.executor.userClassPathFirst 。当我尝试并标记为实验时,它并没有完全奏效。我想最好的方法是隐藏依赖关系。鉴于Spark及其组件库的数量,使用复杂的Spark应用程序可能会产生很多阴影。

答案 1 :(得分:1)

我和你有同样的例外。基于recommendation,我能够通过按照您的建议着色avro依赖项来解决此异常:

assemblyShadeRules in assembly := Seq( ShadeRule.rename("org.apache.avro.**" -> "latest_avro.@1").inAll )

如果有帮助,这是我的完整build.sbt(除了项目信息):

val sparkVersion = "2.1.0"
val confluentVersion = "3.2.1"

resolvers += "Confluent" at "http://packages.confluent.io/maven"

libraryDependencies ++= Seq(
  "org.scala-lang" % "scala-library" % scalaVersion.value % "provided",
  "org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
  "org.apache.spark" %% "spark-streaming" % sparkVersion % "provided",
  "org.apache.spark" % "spark-streaming-kafka-0-10_2.11" % sparkVersion,
  "org.apache.spark" %% "spark-sql" % sparkVersion % "provided" excludeAll ExclusionRule(organization = "org.scala-lang"),
  "org.apache.avro" % "avro" % "1.8.1" % "provided",
  "com.databricks" %% "spark-avro" % "3.2.0",
  "com.sksamuel.avro4s" %% "avro4s-core" % "1.6.4",
  "io.confluent" % "kafka-avro-serializer" % confluentVersion
)

logBuffered in Test := false

assemblyShadeRules in assembly := Seq(
  ShadeRule.rename("shapeless.**" -> "new_shapeless.@1").inAll,
  ShadeRule.rename("org.apache.avro.**" -> "latest_avro.@1").inAll
)

assemblyMergeStrategy in assembly := {
  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
  case x =>
    val oldStrategy = (assemblyMergeStrategy in assembly).value
    oldStrategy(x)
}