无法找到数据源:parquet,在使用 sbt assembly 构建时

Failed to find data source: parquet, when building with sbt assembly

我有以下代码:

val testRDD: RDD[(String, Vector)] = sc.parallelize(testArray)

val sqlContext = new org.apache.spark.sql.SQLContext(sc)
import sqlContext.implicits._

val df = testRDD.toDF()

df.write.parquet(path)

与以下 build.sbt:

libraryDependencies += "org.apache.spark" %% "spark-core" % "1.6.1"
libraryDependencies += "org.apache.spark" %% "spark-sql" % "1.6.1"
libraryDependencies += "org.apache.spark" %% "spark-mllib" % "1.6.1"

// META-INF discarding
mergeStrategy in assembly <<= (mergeStrategy in assembly) { (old) =>
{
  case "reference.conf" => MergeStrategy.concat
  case PathList("META-INF", xs @ _*) => MergeStrategy.discard
  case x => MergeStrategy.first
}
}

当我用 sbt-assembly 构建它时(我有 addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.12.0")),然后我 运行 它,我得到一个错误:

Exception in thread "main" java.lang.ClassNotFoundException: Failed to find data source: parquet. Please find packages at http://spark-packages.org
    at org.apache.spark.sql.execution.datasources.ResolvedDataSource$.lookupDataSource(ResolvedDataSource.scala:77)
    at org.apache.spark.sql.execution.datasources.ResolvedDataSource$.apply(ResolvedDataSource.scala:219)
    at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:148)
    at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:139)
    at org.apache.spark.sql.DataFrameWriter.parquet(DataFrameWriter.scala:334)
    at InductionService.Application$.main(ParquetTest.scala:65)

但是,如果我使用 IntelliJ Idea 构建它(正常构建,而不是像 sbt 程序集那样的胖 JAR 类的东西),并在 IDE 中调试它,它实际上可以工作。很明显我使用 sbt assembly 构建它的方式有问题,但我不知道如何修复它。有什么想法吗?

我怀疑 build.sbt 中的 merge-inf 丢弃代码可能是原因,但我需要该代码,否则我无法使用 sbt-assembly 进行构建。 (它抱怨重复...)

我遇到了 sae 问题。 META-INF 中的 services 文件夹存在一些合并问题。 我可以通过在 MergeStrategy 上添加一条规则来解决这个问题:

case n if n.contains("services") => MergeStrategy.concat

这就是我所拥有的,现在可以使用了:

val meta = """META.INF(.)*""".r
assemblyMergeStrategy in assembly := {
  case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".html" => MergeStrategy.first
  case n if n.contains("services") => MergeStrategy.concat
  case n if n.startsWith("reference.conf") => MergeStrategy.concat
  case n if n.endsWith(".conf") => MergeStrategy.concat
  case meta(_) => MergeStrategy.discard
  case x => MergeStrategy.first
}

上面的解决方案并不理想,它是部分正确的,你确实需要合并 META-INF/service/.... 带并丢弃 META-INF 中的其他但上面的解决方案是用 case n if n.contains("service")条件太笼统。 如果 任何重复的文件 ,不仅仅是在 META-INF 中包含单词 'service',这是很常见的,该文件将被连接起来,包括 class 文件。 如果您的程序或其依赖项之一包含 class,如 com/amazonaws/services/s3/model/AmazonS3Exception,就像我的一样,它会将它们连接起来,导致:

java.lang.ClassFormatError: Extra bytes at the end of class file com/amazonaws/services/s3/model/AmazonS3Exception

最好尽可能地限制 concat 子句,给出一个子句“concat META-INF/services,丢弃任何其他的 META-INF”,这是一种方法:

assemblyMergeStrategy in assembly := {
  case PathList("META-INF", "services",  _*)  => MergeStrategy.concat
  case PathList("META-INF", _*) => MergeStrategy.discard
  case _ => MergeStrategy.first

}