【问题标题】:Why isn't guava being shaded properly in my build.sbt?为什么我的 build.sbt 中没有正确着色番石榴?
【发布时间】:2018-06-03 02:19:42
【问题描述】:

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


Cassandra 和 HDFS 都在内部使用 guava,但由于各种原因,它们都没有隐藏依赖关系。因为番石榴的版本不是二进制兼容的,我在运行时找到NoSuchMethodErrors。

我曾尝试在我的build.sbt 中为番石榴遮光:

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 笑话,人们)。

具体的例外是

[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 库本身中重命名。
  • 您可能希望遵循与此类似的流程:manuzhang.github.io/2016/10/15/shading.html
  • 据我所知,使用程序集进行着色有一些限制,我鼓励您在 Coursier github.com/coursier/coursier/tree/master/sbt-shading/src 中查看这个替代着色插件。
  • 哈哈。你可以阅读代码,我的朋友。如果您想要一个示例,该插件的用户就在 coursier 构建中。组装的问题在于它不能传递地遮蔽事物,而快递员可以 IIRC。值得。

标签: hadoop cassandra sbt nosuchmethoderror sbt-assembly


【解决方案1】:

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

如果你想在你的 hadoop 依赖项中隐藏一些库,你可以创建一个只有 hadoop 依赖项的新项目,对库进行隐藏,然后发布一个包含所有隐藏的 hadoop 依赖项的胖 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
我建议使用这样的东西来保留所有不是deduplicate的原始策略

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

【讨论】:

  • 直到今天我还没有来得及构建胖罐子。这是一个非常有用的答案,让我走上了正确的道路。我需要做的唯一更改是fatJar 的合并策略。我需要添加 case PathList("META-INF", "services", "org.apache.hadoop.fs.FileSystem") =&gt; MergeStrategy.filterDistinctLines 以便 hdfs 可以维护所有不同的文件系统。
  • 这个模式来自this answer的 cmets。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-25
  • 1970-01-01
  • 2012-07-18
  • 2012-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多