hadoop - 为什么 Guava 在我的 build.sbt 中没有正确着色?

标签 hadoop cassandra sbt nosuchmethoderror sbt-assembly

tl;博士:Here是包含问题的存储库。


Cassandra 和 HDFS 都在内部使用 guava,但由于各种原因,它们都没有屏蔽依赖关系。由于 guava 的版本不兼容二进制,因此我在运行时发现 NoSuchMethodError

我尝试在 build.sbt 中自己对 Guava 进行着色:

val HadoopVersion =  "2.6.0-cdh5.11.0"

// ...

val hadoopHdfs = "org.apache.hadoop" % "hadoop-hdfs" % HadoopVersion
val hadoopCommon = "org.apache.hadoop" % "hadoop-common" % HadoopVersion
val hadoopHdfsTest = "org.apache.hadoop" % "hadoop-hdfs" % HadoopVersion % "test" classifier "tests"
val hadoopCommonTest = "org.apache.hadoop" % "hadoop-common" % HadoopVersion % "test" classifier "tests"
val hadoopMiniDFSCluster = "org.apache.hadoop" % "hadoop-minicluster" % HadoopVersion % Test

// ...

assemblyShadeRules in assembly := Seq(
  ShadeRule.rename("com.google.common.**" -> "shade.com.google.common.@1").inLibrary(hadoopHdfs).inProject,
  ShadeRule.rename("com.google.common.**" -> "shade.com.google.common.@1").inLibrary(hadoopCommon).inProject,
  ShadeRule.rename("com.google.common.**" -> "shade.com.google.common.@1").inLibrary(hadoopHdfsTest).inProject,
  ShadeRule.rename("com.google.common.**" -> "shade.com.google.common.@1").inLibrary(hadoopCommonTest).inProject,
  ShadeRule.rename("com.google.common.**" -> "shade.com.google.common.@1").inLibrary(hadoopMiniDFSCluster).inProject
)

assemblyJarName in assembly := s"${name.value}-${version.value}.jar"

assemblyMergeStrategy in assembly := {
  case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
  case _ => MergeStrategy.first
}

但是运行时异常仍然存在(哈——这是一个 Cassandra 笑话, friend 们)。

具体的异常(exception)是

[info] HdfsEntitySpec *** ABORTED ***
[info]   java.lang.NoSuchMethodError: com.google.common.base.Objects.toStringHelper(Ljava/lang/Object;)Lcom/google/common/base/Objects$ToStringHelper;
[info]   at org.apache.hadoop.metrics2.lib.MetricsRegistry.toString(MetricsRegistry.java:406)
[info]   at java.lang.String.valueOf(String.java:2994)
[info]   at java.lang.StringBuilder.append(StringBuilder.java:131)
[info]   at org.apache.hadoop.ipc.metrics.RetryCacheMetrics.<init>(RetryCacheMetrics.java:46)
[info]   at org.apache.hadoop.ipc.metrics.RetryCacheMetrics.create(RetryCacheMetrics.java:53)
[info]   at org.apache.hadoop.ipc.RetryCache.<init>(RetryCache.java:202)
[info]   at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.initRetryCache(FSNamesystem.java:1038)
[info]   at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.<init>(FSNamesystem.java:949)
[info]   at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.<init>(FSNamesystem.java:796)
[info]   at org.apache.hadoop.hdfs.server.namenode.NameNode.format(NameNode.java:1040)
[info]   ...

如何正确遮蔽 Guava 以阻止运行时错误?

最佳答案

着色规则仅在您构建 fat jar 子时适用。它不会在其他 sbt 任务中应用。

如果您想遮蔽 hadoop 依赖项中的某些库,您可以创建一个仅包含 hadoop 依赖项的新项目,遮蔽库,然后发布一个包含所有遮蔽 hadoop 依赖项的 fat jar。

这不是一个完美的解决方案,因为新的 hadoop jar 中的所有依赖项对于谁使用它们来说都是“未知的”,并且您需要手动处理冲突。

这是您在 build.sbt 中发布胖 hadoop jar 所需的代码 (使用您的代码和 sbt 程序集 docs ):

val HadoopVersion =  "2.6.0-cdh5.11.0"

val hadoopHdfs = "org.apache.hadoop" % "hadoop-hdfs" % HadoopVersion
val hadoopCommon = "org.apache.hadoop" % "hadoop-common" % HadoopVersion
val hadoopHdfsTest = "org.apache.hadoop" % "hadoop-hdfs" % HadoopVersion classifier "tests"
val hadoopCommonTest = "org.apache.hadoop" % "hadoop-common" % HadoopVersion %  classifier "tests"
val hadoopMiniDFSCluster = "org.apache.hadoop" % "hadoop-minicluster" % HadoopVersion 

lazy val fatJar = project
  .enablePlugins(AssemblyPlugin)
  .settings(
    libraryDependencies ++= Seq(
        hadoopHdfs,
        hadoopCommon,
        hadoopHdfsTest,
        hadoopCommonTest,
        hadoopMiniDFSCluster
    ),
      assemblyShadeRules in assembly := Seq(
      ShadeRule.rename("com.google.common.**" -> "shade.@0").inAll
    ),
    assemblyMergeStrategy in assembly := {
      case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
      case _ => MergeStrategy.first
    },
    artifact in (Compile, assembly) := {
      val art = (artifact in (Compile, assembly)).value
      art.withClassifier(Some("assembly"))
    },
    addArtifact(artifact in (Compile, assembly), assembly),
    crossPaths := false, // Do not append Scala versions to the generated artifacts
    autoScalaLibrary := false, // This forbids including Scala related libraries into the dependency
    skip in publish := true
  )

lazy val shaded_hadoop = project
  .settings(
    name := "shaded-hadoop",
    packageBin in Compile := (assembly in (fatJar, Compile)).value
  )

我还没有测试过,但这就是它的要点。


我想指出我注意到的另一个问题,您的合并策略可能会给您带来问题,因为您想对某些文件应用不同的策略。查看默认策略here .
我建议使用类似的方法来保留所有非重复数据删除

的原始策略
assemblyMergeStrategy in assembly := {
          entry: String => {
            val strategy = (assemblyMergeStrategy in assembly).value(entry)
            if (strategy == MergeStrategy.deduplicate) MergeStrategy.first
            else strategy
          }
      }

关于hadoop - 为什么 Guava 在我的 build.sbt 中没有正确着色?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47907446/

相关文章:

java - 在 Play 2 框架上配置 QueryDSL

hadoop - 在包含空间的路径中使用jar设置mapreduce.job.jar

java - Hadoop 程序在 "java -jar"上运行良好,但在 "hadoop jar"下运行良好

hadoop - 映射减少事件时,有没有办法打开Unix命令?

Cassandra : COPY data with cql function on column

scala - 如何在eclipse中调试sbt项目

shell - 如何获取基于 HIVE-SQOOP 的批处理作业的异常、错误、日志?

node.js - NodeJS 上的 Cassandra 客户端错误

java - 如何从 Cassandra 表中检索数据类型为 "list"的列?

scala - sbt 直接 git 源依赖项 - 不获取传递库依赖项?